diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index c4e50e8f5e0..4d2f268f2ae 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -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()) { - 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); diff --git a/test/lit/passes/optimize-instructions-fast-math.wast b/test/lit/passes/optimize-instructions-fast-math.wast new file mode 100644 index 00000000000..8e9a42a411c --- /dev/null +++ b/test/lit/passes/optimize-instructions-fast-math.wast @@ -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) + ) + ) + ) + ) + ) +) diff --git a/test/lit/passes/optimize-instructions-global-effects-idempotent.wast b/test/lit/passes/optimize-instructions-global-effects-idempotent.wast index 8c5365260a9..76f0a71f326 100644 --- a/test/lit/passes/optimize-instructions-global-effects-idempotent.wast +++ b/test/lit/passes/optimize-instructions-global-effects-idempotent.wast @@ -94,39 +94,35 @@ ) (module - ;; CHECK: (global $f (mut f32) (f32.const -3)) - (global $f (mut f32) (f32.const -3)) + ;; CHECK: (global $g (mut i32) (i32.const 0)) + (global $g (mut i32) (i32.const 0)) - ;; CHECK: (func $potent (type $0) (result f32) - ;; CHECK-NEXT: (global.set $f - ;; CHECK-NEXT: (f32.add - ;; CHECK-NEXT: (global.get $f) - ;; CHECK-NEXT: (f32.const 2) + ;; CHECK: (func $potent (type $0) (result i32) + ;; CHECK-NEXT: (global.set $g + ;; CHECK-NEXT: (i32.add + ;; CHECK-NEXT: (global.get $g) + ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (global.get $f) + ;; CHECK-NEXT: (global.get $g) ;; CHECK-NEXT: ) - (func $potent (result f32) - (global.set $f (f32.add (global.get $f) (f32.const 2))) - (global.get $f) + (func $potent (result i32) + (global.set $g (i32.add (global.get $g) (i32.const 1))) + (global.get $g) ) - ;; CHECK: (func $function-effects-interfere (type $0) (result f32) - ;; CHECK-NEXT: (f32.abs - ;; CHECK-NEXT: (f32.mul - ;; CHECK-NEXT: (call $potent) - ;; CHECK-NEXT: (call $potent) - ;; CHECK-NEXT: ) + ;; CHECK: (func $function-effects-interfere (type $0) (result i32) + ;; CHECK-NEXT: (i32.eq + ;; CHECK-NEXT: (call $potent) + ;; CHECK-NEXT: (call $potent) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - (func $function-effects-interfere (result f32) + (func $function-effects-interfere (result i32) ;; Even knowing the effects of the call, we should not be able to optimize ;; here because the calls will return different values. - (f32.abs - (f32.mul - (call $potent) - (call $potent) - ) + (i32.eq + (call $potent) + (call $potent) ) ) ) diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index dd2d0daa4d5..d99fedb6cd7 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -5,17 +5,8 @@ ;; CHECK: (type $0 (func (param i32 i64))) (type $0 (func (param i32 i64))) - ;; 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))) - (memory 0) - ;; CHECK: (global $g-f32 (mut f32) (f32.const -3)) - (global $g-f32 (mut f32) (f32.const -3)) - ;; CHECK: (func $and-and (param $i1 i32) (result i32) ;; CHECK-NEXT: (i32.and ;; CHECK-NEXT: (local.get $i1) @@ -15256,26 +15247,32 @@ ) ;; CHECK: (func $optimize-float-points (param $x0 f64) (param $x1 f64) (param $y0 f32) (param $y1 f32) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (f64.mul - ;; CHECK-NEXT: (local.get $x0) - ;; CHECK-NEXT: (local.get $x0) + ;; CHECK-NEXT: (f64.abs + ;; CHECK-NEXT: (f64.mul + ;; CHECK-NEXT: (local.get $x0) + ;; CHECK-NEXT: (local.get $x0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (f32.mul - ;; CHECK-NEXT: (local.get $y0) - ;; CHECK-NEXT: (local.get $y0) + ;; CHECK-NEXT: (f32.abs + ;; CHECK-NEXT: (f32.mul + ;; CHECK-NEXT: (local.get $y0) + ;; CHECK-NEXT: (local.get $y0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (f64.mul - ;; CHECK-NEXT: (f64.add - ;; CHECK-NEXT: (local.get $x0) - ;; CHECK-NEXT: (local.get $x1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (f64.add - ;; CHECK-NEXT: (local.get $x0) - ;; CHECK-NEXT: (local.get $x1) + ;; CHECK-NEXT: (f64.abs + ;; CHECK-NEXT: (f64.mul + ;; CHECK-NEXT: (f64.add + ;; CHECK-NEXT: (local.get $x0) + ;; CHECK-NEXT: (local.get $x1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (f64.add + ;; CHECK-NEXT: (local.get $x0) + ;; CHECK-NEXT: (local.get $x1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -15352,26 +15349,32 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (f64.div - ;; CHECK-NEXT: (local.get $x0) - ;; CHECK-NEXT: (local.get $x0) + ;; CHECK-NEXT: (f64.abs + ;; CHECK-NEXT: (f64.div + ;; CHECK-NEXT: (local.get $x0) + ;; CHECK-NEXT: (local.get $x0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (f32.div - ;; CHECK-NEXT: (local.get $y0) - ;; CHECK-NEXT: (local.get $y0) + ;; CHECK-NEXT: (f32.abs + ;; CHECK-NEXT: (f32.div + ;; CHECK-NEXT: (local.get $y0) + ;; CHECK-NEXT: (local.get $y0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (f64.div - ;; CHECK-NEXT: (f64.add - ;; CHECK-NEXT: (local.get $x0) - ;; CHECK-NEXT: (local.get $x1) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (f64.add - ;; CHECK-NEXT: (local.get $x0) - ;; CHECK-NEXT: (local.get $x1) + ;; CHECK-NEXT: (f64.abs + ;; CHECK-NEXT: (f64.div + ;; CHECK-NEXT: (f64.add + ;; CHECK-NEXT: (local.get $x0) + ;; CHECK-NEXT: (local.get $x1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (f64.add + ;; CHECK-NEXT: (local.get $x0) + ;; CHECK-NEXT: (local.get $x1) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -15392,35 +15395,35 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (f64.mul - ;; CHECK-NEXT: (local.get $x0) - ;; CHECK-NEXT: (local.get $x0) + ;; CHECK-NEXT: (f64.abs + ;; CHECK-NEXT: (f64.mul + ;; CHECK-NEXT: (local.get $x0) + ;; CHECK-NEXT: (local.get $x0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (f32.mul - ;; CHECK-NEXT: (local.get $y0) - ;; CHECK-NEXT: (local.get $y0) + ;; CHECK-NEXT: (f32.abs + ;; CHECK-NEXT: (f32.mul + ;; CHECK-NEXT: (local.get $y0) + ;; CHECK-NEXT: (local.get $y0) + ;; CHECK-NEXT: ) ;; 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: (f64.div + ;; CHECK-NEXT: (local.get $x0) + ;; CHECK-NEXT: (local.get $x0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (f64.div - ;; CHECK-NEXT: (local.get $x0) - ;; CHECK-NEXT: (local.get $x0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (f32.div - ;; CHECK-NEXT: (local.get $y0) - ;; CHECK-NEXT: (local.get $y0) + ;; CHECK-NEXT: (f32.abs + ;; CHECK-NEXT: (f32.div + ;; CHECK-NEXT: (local.get $y0) + ;; CHECK-NEXT: (local.get $y0) + ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: (drop @@ -15455,7 +15458,7 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) (func $optimize-float-points (param $x0 f64) (param $x1 f64) (param $y0 f32) (param $y1 f32) - ;; abs(x) * abs(x) ==> x * x + ;; abs(x) * abs(x) ==> abs(x * x) (drop (f64.mul (f64.abs (local.get $x0)) (f64.abs (local.get $x0)) @@ -15515,7 +15518,7 @@ ) )) - ;; abs(x) / abs(x) ==> x / x + ;; abs(x) / abs(x) ==> abs(x / x) (drop (f64.div (f64.abs (local.get $x0)) (f64.abs (local.get $x0)) @@ -15539,7 +15542,9 @@ (f32.abs (local.get $y0)) )) - ;; abs(x * x) ==> x * x + ;; abs(x * x) and abs(x / x) are not optimized without fast-math: x * x and + ;; x / x may be a NaN with the sign bit set, which abs would clear. See + ;; optimize-instructions-fast-math.wast. (drop (f64.abs (f64.mul (local.get $x0) @@ -15552,17 +15557,6 @@ (local.get $y0) ) )) - ;; 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 $x0) @@ -15590,296 +15584,6 @@ )) ) - ;; 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) - ) - ) - ) - ) - ) ;; CHECK: (func $ternary (param $x i32) (param $y i32) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (i32.eqz diff --git a/test/lit/passes/optimize-instructions_idempotent.wast b/test/lit/passes/optimize-instructions_idempotent.wast index abc999a133e..a0f22d03050 100644 --- a/test/lit/passes/optimize-instructions_idempotent.wast +++ b/test/lit/passes/optimize-instructions_idempotent.wast @@ -1,8 +1,9 @@ ;; 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 +;; RUN: foreach %s %t wasm-opt --optimize-instructions --fast-math -all -S -o - | filecheck %s ;; Idempotent-marked functions can be assumed to always return the same value. +;; Many tests here use abs(x * x) ==> x * x, which requires fast-math. (module ;; CHECK: (import "a" "b" (func $import (type $2) (result f32))) @@ -84,7 +85,7 @@ ;; 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. + ;; remove the abs. (drop (f32.abs (f32.mul