blob: 0caf2200fb81a73ec0dbcd7efc30d1eade76dcd8 [file] [edit]
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: foreach %s %t wasm-opt --remove-unused-module-elements -all -S -o - | filecheck %s
;; RUN: foreach %s %t wasm-opt --remove-unused-module-elements -tnh -all -S -o - | filecheck %s --check-prefix TNH__
(module
;; CHECK: (type $i32 (func (result i32)))
;; TNH__: (type $i32 (func (result i32)))
(type $i32 (func (result i32)))
;; CHECK: (type $1 (func))
;; CHECK: (type $2 (func (result i64)))
;; CHECK: (table $table 1 funcref (ref.func $i32))
;; TNH__: (type $1 (func))
;; TNH__: (table $table 1 funcref (ref.func $i32))
(table $table 1 funcref (ref.func $i32))
;; CHECK: (elem $elem (i32.const 0) $i64)
(elem $elem (i32.const 0) func $i64)
;; CHECK: (export "caller" (func $caller))
;; TNH__: (export "caller" (func $caller))
(export "caller" (func $caller))
;; CHECK: (func $caller (type $1)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call_indirect $table (type $i32)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; TNH__: (func $caller (type $1)
;; TNH__-NEXT: (drop
;; TNH__-NEXT: (call_indirect $table (type $i32)
;; TNH__-NEXT: (i32.const 0)
;; TNH__-NEXT: )
;; TNH__-NEXT: )
;; TNH__-NEXT: )
(func $caller
;; This call traps on the wrong type: we call $i32, but the elem puts an $i64
;; there. When optimizing, we should not remove the elem, as then we would
;; end up calling the default value of $i32, which would not trap.
;;
;; However, when we assume traps never happen, we can still optimize here
;; and remove the elem.
(drop
(call_indirect $table (type $i32)
(i32.const 0)
)
)
)
;; CHECK: (func $i32 (type $i32) (result i32)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; TNH__: (func $i32 (type $i32) (result i32)
;; TNH__-NEXT: (i32.const 0)
;; TNH__-NEXT: )
(func $i32 (type $i32) (result i32)
(i32.const 0)
)
;; CHECK: (func $i64 (type $2) (result i64)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $i64 (result i64)
(unreachable)
)
)
;; As above, but without an initial value in the table.
(module
;; CHECK: (type $0 (func))
;; CHECK: (type $i32 (func (result i32)))
;; TNH__: (type $0 (func))
;; TNH__: (type $i32 (func (result i32)))
(type $i32 (func (result i32)))
;; CHECK: (table $table 1 funcref)
;; TNH__: (table $table 1 funcref)
(table $table 1 funcref)
(elem $elem (i32.const 0) func $i64)
;; CHECK: (export "caller" (func $caller))
;; TNH__: (export "caller" (func $caller))
(export "caller" (func $caller))
;; CHECK: (func $caller (type $0)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call_indirect $table (type $i32)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; TNH__: (func $caller (type $0)
;; TNH__-NEXT: (drop
;; TNH__-NEXT: (call_indirect $table (type $i32)
;; TNH__-NEXT: (i32.const 0)
;; TNH__-NEXT: )
;; TNH__-NEXT: )
;; TNH__-NEXT: )
(func $caller
;; Without an initial value in the table, we can optimize (removing the
;; elem) even without TNH: we still trap, just on a null rather than the
;; wrong type.
(drop
(call_indirect $table (type $i32)
(i32.const 0)
)
)
)
(func $i32 (type $i32) (result i32)
(i32.const 0)
)
(func $i64 (result i64)
(unreachable)
)
)
;; Overwrite the first segment's function with a null. The null must be kept
;; around, so we trap - if traps are possible. As a result, elem $second is
;; removed in TNH, but not otherwise. elem $third, on the other hand, can be
;; removed in both cases, as it contains no nulls, only a function of another
;; type - but we do not actually remove it in CHECK, as given the presence of
;; overlapping segments, we give up on analyzing the precise overlap, and do not
;; remove segments from the table.
(module
;; CHECK: (type $func (func))
;; TNH__: (type $func (func))
(type $func (func))
;; CHECK: (type $other (func (result i32)))
(type $other (func (result i32)))
;; CHECK: (type $2 (func (param i32)))
;; CHECK: (table $table 6 6 funcref)
;; TNH__: (type $1 (func (param i32)))
;; TNH__: (table $table 6 6 funcref)
(table $table 6 6 funcref)
;; CHECK: (elem $first (i32.const 0) $func)
;; TNH__: (elem $first (i32.const 0) $func)
(elem $first (i32.const 0) $func)
;; CHECK: (elem $second (table $table) (i32.const 0) funcref (item (ref.null nofunc)))
(elem $second (table $table) (i32.const 0) funcref (item (ref.null nofunc)))
;; CHECK: (elem $third (i32.const 1) $other)
(elem $third (i32.const 1) $other)
;; CHECK: (export "export" (func $export))
;; CHECK: (func $func (type $func)
;; CHECK-NEXT: )
;; TNH__: (export "export" (func $export))
;; TNH__: (func $func (type $func)
;; TNH__-NEXT: )
(func $func (type $func)
)
;; CHECK: (func $other (type $other) (result i32)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $other (type $other) (result i32)
(i32.const 42)
)
;; CHECK: (func $export (type $2) (param $x i32)
;; CHECK-NEXT: (call_indirect $table (type $func)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; TNH__: (func $export (type $1) (param $x i32)
;; TNH__-NEXT: (call_indirect $table (type $func)
;; TNH__-NEXT: (local.get $x)
;; TNH__-NEXT: )
;; TNH__-NEXT: )
(func $export (export "export") (param $x i32)
;; Call with an unknown index, so we do not use any tricks about known
;; indexes in the table.
(call_indirect $table (type $func)
(local.get $x)
)
)
)
;; Similar, but overwrite not with a null, but with a function of the wrong type
;; for the call_indirect. Like with a null, we must write that wrongly-typed
;; function, to preserve the trap, if traps are possible, so when not TNH, we
;; must keep elem $second around.
(module
;; CHECK: (type $func (func))
;; TNH__: (type $func (func))
(type $func (func))
;; CHECK: (type $other (func (result i32)))
(type $other (func (result i32)))
;; CHECK: (type $2 (func (param i32)))
;; CHECK: (table $table 6 6 funcref)
;; TNH__: (type $1 (func (param i32)))
;; TNH__: (table $table 6 6 funcref)
(table $table 6 6 funcref)
;; CHECK: (elem $first (i32.const 0) $func)
;; TNH__: (elem $first (i32.const 0) $func)
(elem $first (i32.const 0) $func)
;; CHECK: (elem $second (i32.const 0) $other)
(elem $second (i32.const 0) $other)
;; CHECK: (export "export" (func $export))
;; CHECK: (func $func (type $func)
;; CHECK-NEXT: )
;; TNH__: (export "export" (func $export))
;; TNH__: (func $func (type $func)
;; TNH__-NEXT: )
(func $func (type $func)
)
;; CHECK: (func $other (type $other) (result i32)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $other (type $other) (result i32)
(i32.const 42)
)
;; CHECK: (func $export (type $2) (param $x i32)
;; CHECK-NEXT: (call_indirect $table (type $func)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; TNH__: (func $export (type $1) (param $x i32)
;; TNH__-NEXT: (call_indirect $table (type $func)
;; TNH__-NEXT: (local.get $x)
;; TNH__-NEXT: )
;; TNH__-NEXT: )
(func $export (export "export") (param $x i32)
(call_indirect $table (type $func)
(local.get $x)
)
)
)
;; As the last testcase, but now there is no segment overlap, so it is safe to
;; always remove elem $second.
(module
;; CHECK: (type $func (func))
;; TNH__: (type $func (func))
(type $func (func))
(type $other (func (result i32)))
;; CHECK: (type $1 (func (param i32)))
;; CHECK: (table $table 6 6 funcref)
;; TNH__: (type $1 (func (param i32)))
;; TNH__: (table $table 6 6 funcref)
(table $table 6 6 funcref)
;; CHECK: (elem $first (i32.const 0) $func)
;; TNH__: (elem $first (i32.const 0) $func)
(elem $first (i32.const 0) $func)
(elem $second (i32.const 1) $other) ;; the offset here changed from 0 to 1
;; CHECK: (export "export" (func $export))
;; CHECK: (func $func (type $func)
;; CHECK-NEXT: )
;; TNH__: (export "export" (func $export))
;; TNH__: (func $func (type $func)
;; TNH__-NEXT: )
(func $func (type $func)
)
(func $other (type $other) (result i32)
(i32.const 42)
)
;; CHECK: (func $export (type $1) (param $x i32)
;; CHECK-NEXT: (call_indirect $table (type $func)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; TNH__: (func $export (type $1) (param $x i32)
;; TNH__-NEXT: (call_indirect $table (type $func)
;; TNH__-NEXT: (local.get $x)
;; TNH__-NEXT: )
;; TNH__-NEXT: )
(func $export (export "export") (param $x i32)
(call_indirect $table (type $func)
(local.get $x)
)
)
)
;; As above, but with non-adjacent segment offsets: before we had [0, 1),
;; [1, 2), and now the second is [2, 3). We can optimize away elem $second
;; as there is no overlap.
(module
;; CHECK: (type $func (func))
;; TNH__: (type $func (func))
(type $func (func))
(type $other (func (result i32)))
;; CHECK: (type $1 (func (param i32)))
;; CHECK: (table $table 6 6 funcref)
;; TNH__: (type $1 (func (param i32)))
;; TNH__: (table $table 6 6 funcref)
(table $table 6 6 funcref)
;; CHECK: (elem $first (i32.const 0) $func)
;; TNH__: (elem $first (i32.const 0) $func)
(elem $first (i32.const 0) $func)
(elem $second (i32.const 2) $other) ;; the offset here changed
;; CHECK: (export "export" (func $export))
;; CHECK: (func $func (type $func)
;; CHECK-NEXT: )
;; TNH__: (export "export" (func $export))
;; TNH__: (func $func (type $func)
;; TNH__-NEXT: )
(func $func (type $func)
)
(func $other (type $other) (result i32)
(i32.const 42)
)
;; CHECK: (func $export (type $1) (param $x i32)
;; CHECK-NEXT: (call_indirect $table (type $func)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; TNH__: (func $export (type $1) (param $x i32)
;; TNH__-NEXT: (call_indirect $table (type $func)
;; TNH__-NEXT: (local.get $x)
;; TNH__-NEXT: )
;; TNH__-NEXT: )
(func $export (export "export") (param $x i32)
(call_indirect $table (type $func)
(local.get $x)
)
)
)
;; As the last testcase, but the first segment is long enough to cause overlap:
;; we had [0, 1), [2, 3), and now we have [0, 3), [2, 3). We cannot remove
;; elem $second.
(module
;; CHECK: (type $func (func))
;; TNH__: (type $func (func))
(type $func (func))
;; CHECK: (type $other (func (result i32)))
(type $other (func (result i32)))
;; CHECK: (type $2 (func (param i32)))
;; CHECK: (table $table 6 6 funcref)
;; TNH__: (type $1 (func (param i32)))
;; TNH__: (table $table 6 6 funcref)
(table $table 6 6 funcref)
;; CHECK: (elem $first (i32.const 0) $func $func $func)
;; TNH__: (elem $first (i32.const 0) $func $func $func)
(elem $first (i32.const 0) $func $func $func) ;; the length here changed
;; CHECK: (elem $second (i32.const 2) $other)
(elem $second (i32.const 2) $other)
;; CHECK: (export "export" (func $export))
;; CHECK: (func $func (type $func)
;; CHECK-NEXT: )
;; TNH__: (export "export" (func $export))
;; TNH__: (func $func (type $func)
;; TNH__-NEXT: )
(func $func (type $func)
)
;; CHECK: (func $other (type $other) (result i32)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $other (type $other) (result i32)
(i32.const 42)
)
;; CHECK: (func $export (type $2) (param $x i32)
;; CHECK-NEXT: (call_indirect $table (type $func)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; TNH__: (func $export (type $1) (param $x i32)
;; TNH__-NEXT: (call_indirect $table (type $func)
;; TNH__-NEXT: (local.get $x)
;; TNH__-NEXT: )
;; TNH__-NEXT: )
(func $export (export "export") (param $x i32)
(call_indirect $table (type $func)
(local.get $x)
)
)
)