Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/passes/OptimizeInstructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1126,10 +1126,13 @@ struct OptimizeInstructions
return replaceCurrent(curr);
}
}
// abs(x * x) ==> x * x
// abs(x / x) ==> x / x
if (auto* binary = curr->value->dynCast<Binary>()) {
if ((binary->op == Abstract::getBinary(binary->type, Abstract::Mul) ||
// abs(x * x) ==> x * x
// abs(x / x) ==> x / x
// only for fast math, as the result may be a NaN with the sign bit set,
// which abs would clear.
if (fastMath &&
(binary->op == Abstract::getBinary(binary->type, Abstract::Mul) ||
binary->op == Abstract::getBinary(binary->type, Abstract::DivS)) &&
areConsecutiveInputsEqual(binary->left, binary->right)) {
return replaceCurrent(binary);
Expand Down
379 changes: 379 additions & 0 deletions test/lit/passes/optimize-instructions-fast-math.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,379 @@
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: wasm-opt %s --optimize-instructions --fast-math --mvp-features -S -o - | filecheck %s

;; Optimizations that require fast-math.

(module
;; CHECK: (import "a" "b" (func $get-f64 (result f64)))
(import "a" "b" (func $get-f64 (result f64)))

;; CHECK: (import "a" "c" (func $set-i32 (param i32)))
(import "a" "c" (func $set-i32 (param i32)))

;; CHECK: (global $g-f32 (mut f32) (f32.const -3))
(global $g-f32 (mut f32) (f32.const -3))

;; CHECK: (func $abs-mul-div (param $x f64) (param $y f32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (f64.mul
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (f32.mul
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (f64.abs
;; CHECK-NEXT: (f64.mul
;; CHECK-NEXT: (call $get-f64)
;; CHECK-NEXT: (call $get-f64)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (f64.div
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (f32.div
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $abs-mul-div (param $x f64) (param $y f32)
;; abs(x * x) ==> x * x
(drop (f64.abs
(f64.mul
(local.get $x)
(local.get $x)
)
))
(drop (f32.abs
(f32.mul
(local.get $y)
(local.get $y)
)
))
;; This one cannot be optimized as the runtime values may differ: the calls
;; are "generative" in that identical syntactic calls may emit different
;; results.
(drop (f64.abs
(f64.mul
(call $get-f64)
(call $get-f64)
)
))

;; abs(x / x) ==> x / x
(drop (f64.abs
(f64.div
(local.get $x)
(local.get $x)
)
))
(drop (f32.abs
(f32.div
(local.get $y)
(local.get $y)
)
))
)

;; CHECK: (func $optimize-float-points-fallthrough (param $x f64) (param $xb f64) (param $y f32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (f32.mul
;; CHECK-NEXT: (block (result f32)
;; CHECK-NEXT: (call $set-i32
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: (block (result f32)
;; CHECK-NEXT: (call $set-i32
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $optimize-float-points-fallthrough (param $x f64) (param $xb f64) (param $y f32)
;; abs(x * x) ==> x * x , as in the previous function.
;;
;; The fallthrough values here are identical, so we can optimize away the
;; f32.abs despite the effects in both (and even different-looking effects).
(drop (f32.abs
(f32.mul
(block (result f32)
(call $set-i32
(i32.const 42)
)
(local.get $y)
)
(block (result f32)
(call $set-i32
(i32.const 1337)
)
(local.get $y)
)
)
))
)
;; CHECK: (func $optimize-float-points-fallthrough-b (param $x f64) (param $xb f64) (param $y f32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (f64.abs
;; CHECK-NEXT: (f64.mul
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (call $set-i32
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $get-f64)
;; CHECK-NEXT: )
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (call $set-i32
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: )
;; CHECK-NEXT: (call $get-f64)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $optimize-float-points-fallthrough-b (param $x f64) (param $xb f64) (param $y f32)
;; But generative effects in the fallthrough values themselves block us.
(drop (f64.abs
(f64.mul
(block (result f64)
(call $set-i32
(i32.const 42)
)
(call $get-f64) ;; this changed
)
(block (result f64)
(call $set-i32
(i32.const 1337)
)
(call $get-f64) ;; this changed
)
)
))
)
;; CHECK: (func $optimize-float-points-fallthrough-c (param $x f64) (param $xb f64) (param $y f32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (f64.abs
;; CHECK-NEXT: (f64.mul
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (call $set-i32
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.tee $x
;; CHECK-NEXT: (f64.const 12.34)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (call $set-i32
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $optimize-float-points-fallthrough-c (param $x f64) (param $xb f64) (param $y f32)
;; local.tee/get pairs are ok, but atm we don't look at the fallthrough of
;; the right side (we'd need to consider effects). TODO
(drop (f64.abs
(f64.mul
(block (result f64)
(call $set-i32
(i32.const 42)
)
(local.tee $x ;; this changed
(f64.const 12.34)
)
)
(block (result f64)
(call $set-i32
(i32.const 1337)
)
(local.get $x) ;; this changed
)
)
))
)
;; CHECK: (func $optimize-float-points-fallthrough-cb (param $x f64) (param $xb f64) (param $y f32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (f64.abs
;; CHECK-NEXT: (f64.mul
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (call $set-i32
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.tee $x
;; CHECK-NEXT: (f64.const 12.34)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (local.set $x
;; CHECK-NEXT: (f64.const 13.37)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $optimize-float-points-fallthrough-cb (param $x f64) (param $xb f64) (param $y f32)
;; A conflicting set in the middle is a problem: here we cannot optimize.
(drop (f64.abs
(f64.mul
(block (result f64)
(call $set-i32
(i32.const 42)
)
(local.tee $x
(f64.const 12.34)
)
)
(block (result f64)
(local.set $x ;; this changed
(f64.const 13.37)
)
(local.get $x)
)
)
))
)
;; CHECK: (func $optimize-float-points-fallthrough-cc (param $x f64) (param $xb f64) (param $y f32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (f64.mul
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (call $set-i32
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.tee $x
;; CHECK-NEXT: (f64.const 12.34)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $optimize-float-points-fallthrough-cc (param $x f64) (param $xb f64) (param $y f32)
;; Removing the local.set and the block on the right lets us optimize using
;; the tee/get pair.
(drop (f64.abs
(f64.mul
(block (result f64)
(call $set-i32
(i32.const 42)
)
(local.tee $x
(f64.const 12.34)
)
)
(local.get $x) ;; this moved out
)
))
)
;; CHECK: (func $optimize-float-points-fallthrough-d (param $x f64) (param $xb f64) (param $y f32)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (f64.abs
;; CHECK-NEXT: (f64.mul
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (call $set-i32
;; CHECK-NEXT: (i32.const 42)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.tee $x
;; CHECK-NEXT: (f64.const 12.34)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (block (result f64)
;; CHECK-NEXT: (call $set-i32
;; CHECK-NEXT: (i32.const 1337)
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.get $xb)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $optimize-float-points-fallthrough-d (param $x f64) (param $xb f64) (param $y f32)
;; The wrong local index means we fail again.
(drop (f64.abs
(f64.mul
(block (result f64)
(call $set-i32
(i32.const 42)
)
(local.tee $x
(f64.const 12.34)
)
)
(block (result f64)
(call $set-i32
(i32.const 1337)
)
(local.get $xb) ;; this changed
)
)
))
)
;; CHECK: (func $optimize-float-points-stateful (result f32)
;; CHECK-NEXT: (f32.abs
;; CHECK-NEXT: (f32.mul
;; CHECK-NEXT: (f32.neg
;; CHECK-NEXT: (block (result f32)
;; CHECK-NEXT: (global.set $g-f32
;; CHECK-NEXT: (f32.add
;; CHECK-NEXT: (global.get $g-f32)
;; CHECK-NEXT: (f32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $g-f32)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (f32.neg
;; CHECK-NEXT: (block (result f32)
;; CHECK-NEXT: (global.set $g-f32
;; CHECK-NEXT: (f32.add
;; CHECK-NEXT: (global.get $g-f32)
;; CHECK-NEXT: (f32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (global.get $g-f32)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $optimize-float-points-stateful (result f32)
(f32.abs
(f32.mul
;; LHS
(f32.neg
(block (result f32)
(global.set $g-f32 (f32.add (global.get $g-f32) (f32.const 2)))
(global.get $g-f32)
)
)
;; RHS - The increment of the global causes this to produce a different
;; result than the LHS, so we cannot optimize out the f32.abs.
(f32.neg
(block (result f32)
(global.set $g-f32 (f32.add (global.get $g-f32) (f32.const 2)))
(global.get $g-f32)
)
)
)
)
)
)
Loading
Loading