blob: d5ade65810ce79e1088e81e3ce60d0e11fe376bf [file] [log] [blame]
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: foreach %s %t wasm-opt --optimize-instructions -all -S -o - | filecheck %s
;; Idempotent-marked functions can be assumed to always return the same value.
(module
;; CHECK: (import "a" "b" (func $import (type $2) (result f32)))
(import "a" "b" (func $import (result f32)))
;; CHECK: (@binaryen.idempotent)
;; CHECK-NEXT: (func $idempotent (type $0) (param $x f32) (result f32)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
(@binaryen.idempotent)
(func $idempotent (param $x f32) (result f32)
;; This function is idempotent: same inputs, same outputs.
(local.get $x)
)
;; CHECK: (func $potent (type $0) (param $x f32) (result f32)
;; CHECK-NEXT: (call $import)
;; CHECK-NEXT: )
(func $potent (param $x f32) (result f32)
;; This function is not idempotent - anything might happen here.
(call $import)
)
;; CHECK: (func $test-abs (type $1)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (f32.mul
;; CHECK-NEXT: (call $idempotent
;; CHECK-NEXT: (f32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $idempotent
;; CHECK-NEXT: (f32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (f32.abs
;; CHECK-NEXT: (f32.mul
;; CHECK-NEXT: (call $potent
;; CHECK-NEXT: (f32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $potent
;; CHECK-NEXT: (f32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (f32.abs
;; CHECK-NEXT: (f32.mul
;; CHECK-NEXT: (call $idempotent
;; CHECK-NEXT: (f32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $idempotent
;; CHECK-NEXT: (f32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test-abs
;; These calls are identical, since the second returns the same. We can
;; remove the abs, as multiplying a value by itself is non-negative anyhow.
(drop
(f32.abs
(f32.mul
(call $idempotent
(f32.const 10)
)
(call $idempotent
(f32.const 10)
)
)
)
)
;; But here we can do nothing, as we lack idempotency.
(drop
(f32.abs
(f32.mul
(call $potent
(f32.const 10)
)
(call $potent
(f32.const 10)
)
)
)
)
;; Here we fail as well, as while we have idempotency, the params differ.
(drop
(f32.abs
(f32.mul
(call $idempotent
(f32.const 10)
)
(call $idempotent
(f32.const 20)
)
)
)
)
)
;; CHECK: (func $test-abs-calls (type $1)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (f32.mul
;; CHECK-NEXT: (@binaryen.idempotent)
;; CHECK-NEXT: (call $potent
;; CHECK-NEXT: (f32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: (@binaryen.idempotent)
;; CHECK-NEXT: (call $potent
;; CHECK-NEXT: (f32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (f32.abs
;; CHECK-NEXT: (f32.mul
;; CHECK-NEXT: (@binaryen.idempotent)
;; CHECK-NEXT: (call $potent
;; CHECK-NEXT: (f32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $potent
;; CHECK-NEXT: (f32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (f32.mul
;; CHECK-NEXT: (call $potent
;; CHECK-NEXT: (f32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: (@binaryen.idempotent)
;; CHECK-NEXT: (call $potent
;; CHECK-NEXT: (f32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test-abs-calls
;; Here we succeed, as the calls (as opposed to the functions) are marked
;; idempotent.
(drop
(f32.abs
(f32.mul
(@binaryen.idempotent)
(call $potent
(f32.const 10)
)
(@binaryen.idempotent)
(call $potent
(f32.const 10)
)
)
)
)
;; Only one is marked. Marking the first is not enough for us to optimize.
(drop
(f32.abs
(f32.mul
(@binaryen.idempotent)
(call $potent
(f32.const 10)
)
(call $potent
(f32.const 10)
)
)
)
)
;; Marking the second *is* enough for us to optimize.
(drop
(f32.abs
(f32.mul
(call $potent
(f32.const 10)
)
(@binaryen.idempotent)
(call $potent
(f32.const 10)
)
)
)
)
)
)
;; References.
(module
;; CHECK: (type $struct (struct))
(type $struct (struct))
;; CHECK: (import "a" "b" (func $import (type $3) (result eqref)))
(import "a" "b" (func $import (result eqref)))
;; CHECK: (global $g1 (ref $struct) (struct.new_default $struct))
(global $g1 (ref $struct) (struct.new $struct))
;; CHECK: (global $g2 (ref $struct) (struct.new_default $struct))
(global $g2 (ref $struct) (struct.new $struct))
;; CHECK: (global $g-mut (mut (ref $struct)) (struct.new_default $struct))
(global $g-mut (mut (ref $struct)) (struct.new $struct))
;; CHECK: (@binaryen.idempotent)
;; CHECK-NEXT: (func $idempotent (type $1) (param $x eqref) (result eqref)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
(@binaryen.idempotent)
(func $idempotent (param $x eqref) (result eqref)
;; This function is idempotent: same inputs, same outputs.
(local.get $x)
)
;; CHECK: (func $potent (type $1) (param $x eqref) (result eqref)
;; CHECK-NEXT: (call $import)
;; CHECK-NEXT: )
(func $potent (param $x eqref) (result eqref)
;; This function is not idempotent - anything might happen here.
(call $import)
)
;; CHECK: (func $test-ref.eq (type $2)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block (result i32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call $idempotent
;; CHECK-NEXT: (global.get $g1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call $idempotent
;; CHECK-NEXT: (global.get $g1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (call $potent
;; CHECK-NEXT: (global.get $g1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $potent
;; CHECK-NEXT: (global.get $g1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (call $idempotent
;; CHECK-NEXT: (global.get $g1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $idempotent
;; CHECK-NEXT: (global.get $g2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (call $idempotent
;; CHECK-NEXT: (global.get $g-mut)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $idempotent
;; CHECK-NEXT: (global.get $g-mut)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test-ref.eq
;; These calls are identical, since the second returns the same. This
;; results in 1.
(drop
(ref.eq
(call $idempotent
(global.get $g1)
)
(call $idempotent
(global.get $g1)
)
)
)
;; We cannot optimize without idempotency.
(drop
(ref.eq
(call $potent
(global.get $g1)
)
(call $potent
(global.get $g1)
)
)
)
;; We cannot optimize here either - we have idempotency, but params differ.
(drop
(ref.eq
(call $idempotent
(global.get $g1)
)
(call $idempotent
(global.get $g2)
)
)
)
;; We cannot optimize here either - we have idempotency, but the global
;; read is mutable, so the first call might modify it, making it different
;; the second time it is read.
(drop
(ref.eq
(call $idempotent
(global.get $g-mut)
)
(call $idempotent
(global.get $g-mut)
)
)
)
)
;; CHECK: (func $test-ref.eq-nested-calls (type $2)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (call $idempotent
;; CHECK-NEXT: (call $get-struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $idempotent
;; CHECK-NEXT: (call $get-struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.eq
;; CHECK-NEXT: (call $idempotent
;; CHECK-NEXT: (@binaryen.idempotent)
;; CHECK-NEXT: (call $get-struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $idempotent
;; CHECK-NEXT: (@binaryen.idempotent)
;; CHECK-NEXT: (call $get-struct)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test-ref.eq-nested-calls
;; We cannot optimize here - we have idempotency, but the children
;; have effects themselves so we can't tell if they are equal.
(drop
(ref.eq
(call $idempotent
(call $get-struct)
)
(call $idempotent
(call $get-struct)
)
)
)
;; Marking those children as idempotent should help, but we do not handle
;; that yet. TODO
(drop
(ref.eq
(call $idempotent
(@binaryen.idempotent)
(call $get-struct)
)
(call $idempotent
(@binaryen.idempotent)
(call $get-struct)
)
)
)
)
;; CHECK: (func $get-struct (type $4) (result (ref null $struct))
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
(func $get-struct (result (ref null $struct))
;; Helper for above
(unreachable)
)
)