diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 0a4b88a2ba0..9d8c156cfeb 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -3932,9 +3932,11 @@ void BinaryenSetZeroFilledMemory(bool on) { globalPassOptions.zeroFilledMemory = on != 0; } -bool BinaryenGetFastMath(void) { return globalPassOptions.fastMath; } +bool BinaryenGetFastMath(void) { return globalPassOptions.fastMath.ignoreNaNs; } -void BinaryenSetFastMath(bool value) { globalPassOptions.fastMath = value; } +void BinaryenSetFastMath(bool value) { + globalPassOptions.fastMath.ignoreNaNs = value; +} const char* BinaryenGetPassArgument(const char* key) { assert(key); diff --git a/src/pass.h b/src/pass.h index cc63971fe91..cb641dffcdc 100644 --- a/src/pass.h +++ b/src/pass.h @@ -89,6 +89,12 @@ struct InliningOptions { Index partialInliningIfs = 0; }; +struct FastMathOptions { + // Allow optimizing under the assumption that NaNs don't happen. This lets us + // ignore all the NaN-related corner cases. + bool ignoreNaNs = false; +}; + struct PassOptions { // Run passes in debug mode, doing extra validation and timing checks. bool debug = false; @@ -140,11 +146,9 @@ struct PassOptions { // many cases. bool lowMemoryUnused = false; enum { LowMemoryBound = 1024 }; - // Whether to allow "loose" math semantics, ignoring corner cases with NaNs - // and assuming math follows the algebraic rules for associativity and so - // forth (which IEEE floats do not, strictly speaking). This is inspired by - // gcc/clang's -ffast-math flag. - bool fastMath = false; + // Whether to allow "loose" math semantics of various kinds. This is inspired + // by gcc/clang's -ffast-math flags. + FastMathOptions fastMath; // Whether to assume that an imported memory is zero-initialized. Without // this, we can do fewer optimizations on memory segments, because if memory // *was* modified then the wasm's segments may trample those previous diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index f23c119eca3..fbf72d041c3 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -205,10 +205,10 @@ struct OptimizeInstructions Pass* create() override { return new OptimizeInstructions; } - bool fastMath; + bool ignoreNaNs; void doWalkFunction(Function* func) { - fastMath = getPassOptions().fastMath; + ignoreNaNs = getPassOptions().fastMath.ignoreNaNs; // First, scan locals. { @@ -967,7 +967,7 @@ struct OptimizeInstructions } // abs(0 - x) ==> abs(x), // only for fast math - if (fastMath && + if (ignoreNaNs && binary->op == Abstract::getBinary(binary->type, Abstract::Sub)) { if (auto* c = binary->left->dynCast()) { if (c->value.isZero()) { @@ -2812,7 +2812,7 @@ struct OptimizeInstructions curr->op = Abstract::getBinary(type, Add); right->value = right->value.neg(); return curr; - } else if (fastMath) { + } else if (ignoreNaNs) { // x - 0.0 ==> x return curr->left; } @@ -2834,7 +2834,7 @@ struct OptimizeInstructions { // x + (-0.0) ==> x double value; - if (fastMath && matches(curr, binary(Add, any(), fval(&value))) && + if (ignoreNaNs && matches(curr, binary(Add, any(), fval(&value))) && value == 0.0 && std::signbit(value)) { return curr->left; } @@ -2848,10 +2848,10 @@ struct OptimizeInstructions return curr; } // x * -1.0 ==> - // -x, if fastMath == true - // -0.0 - x, if fastMath == false + // -x, if ignoreNaNs == true + // -0.0 - x, if ignoreNaNs == false if (matches(curr, binary(Mul, any(), fval(-1.0)))) { - if (fastMath) { + if (ignoreNaNs) { return builder.makeUnary(Abstract::getUnary(type, Neg), left); } // x * -1.0 ==> -0.0 - x @@ -2863,7 +2863,7 @@ struct OptimizeInstructions if (matches(curr, binary(Mul, any(&left), constant(1))) || matches(curr, binary(DivS, any(&left), constant(1))) || matches(curr, binary(DivU, any(&left), constant(1)))) { - if (curr->type.isInteger() || fastMath) { + if (curr->type.isInteger() || ignoreNaNs) { return left; } } diff --git a/src/tools/optimization-options.h b/src/tools/optimization-options.h index 8d526f92fac..d0a3822e1c2 100644 --- a/src/tools/optimization-options.h +++ b/src/tools/optimization-options.h @@ -205,12 +205,21 @@ struct OptimizationOptions : public ToolOptions { [this](Options*, const std::string&) { passOptions.lowMemoryUnused = true; }) - .add( - "--fast-math", - "-ffm", - "Optimize floats without handling corner cases of NaNs and rounding", - Options::Arguments::Zero, - [this](Options*, const std::string&) { passOptions.fastMath = true; }) + .add("--fast-math", + "-ffm", + "Optimize floats without handling various corner cases. This " + "enables all fast-math flags", + Options::Arguments::Zero, + [this](Options*, const std::string&) { + passOptions.fastMath.ignoreNaNs = true; + }) + .add("--fast-math-ignore-nans", + "-fmin", + "Optimize floats without handling corner cases of NaNs", + Options::Arguments::Zero, + [this](Options*, const std::string&) { + passOptions.fastMath.ignoreNaNs = true; + }) .add("--zero-filled-memory", "-uim", "Assume that an imported memory will be zero-initialized", diff --git a/test/lit/help/optimization-opts.test b/test/lit/help/optimization-opts.test index 34559ab0a69..10af4743715 100644 --- a/test/lit/help/optimization-opts.test +++ b/test/lit/help/optimization-opts.test @@ -184,9 +184,8 @@ ;; CHECK-NEXT: memory is not used by the ;; CHECK-NEXT: application ;; CHECK-NEXT: -;; CHECK-NEXT: --fast-math,-ffm Optimize floats without handling -;; CHECK-NEXT: corner cases of NaNs and -;; CHECK-NEXT: rounding +;; CHECK-NEXT: --fast-math-ignore-nans,-fmin Optimize floats without handling +;; CHECK-NEXT: corner cases of NaNs ;; CHECK-NEXT: ;; CHECK-NEXT: --zero-filled-memory,-uim Assume that an imported memory ;; CHECK-NEXT: will be zero-initialized diff --git a/test/lit/passes/O_fast-math.wast b/test/lit/passes/O_fast-math.wast index 4d0224f6ce3..3e5fa8ebcca 100644 --- a/test/lit/passes/O_fast-math.wast +++ b/test/lit/passes/O_fast-math.wast @@ -1,7 +1,8 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. ;; NOTE: This test was ported using port_test.py and could be cleaned up. -;; RUN: foreach %s %t wasm-opt -O --fast-math -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt -O --fast-math-ignore-nans -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt -O --fast-math -S -o - | filecheck %s --check-prefix GENRL ;; with fast-math we can optimize some of these patterns (module @@ -157,3 +158,71 @@ ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + +;; GENRL: (type $none_=>_f32 (func (result f32))) + +;; GENRL: (type $f32_=>_f32 (func (param f32) (result f32))) + +;; GENRL: (type $f64_=>_f64 (func (param f64) (result f64))) + +;; GENRL: (export "div" (func $0)) + +;; GENRL: (export "mul1" (func $1)) + +;; GENRL: (export "mul2" (func $2)) + +;; GENRL: (export "add1" (func $1)) + +;; GENRL: (export "add2" (func $2)) + +;; GENRL: (export "add3" (func $2)) + +;; GENRL: (export "add4" (func $2)) + +;; GENRL: (export "sub1" (func $1)) + +;; GENRL: (export "sub2" (func $2)) + +;; GENRL: (export "mul_neg_one1" (func $9)) + +;; GENRL: (export "mul_neg_one2" (func $10)) + +;; GENRL: (export "abs_sub_zero1" (func $11)) + +;; GENRL: (export "abs_sub_zero2" (func $12)) + +;; GENRL: (func $0 (; has Stack IR ;) (result f32) +;; GENRL-NEXT: (f32.const -nan:0x23017a) +;; GENRL-NEXT: ) + +;; GENRL: (func $1 (; has Stack IR ;) (result f32) +;; GENRL-NEXT: (f32.const -nan:0x34546d) +;; GENRL-NEXT: ) + +;; GENRL: (func $2 (; has Stack IR ;) (result f32) +;; GENRL-NEXT: (f32.const nan:0x400000) +;; GENRL-NEXT: ) + +;; GENRL: (func $9 (; has Stack IR ;) (param $0 f32) (result f32) +;; GENRL-NEXT: (f32.neg +;; GENRL-NEXT: (local.get $0) +;; GENRL-NEXT: ) +;; GENRL-NEXT: ) + +;; GENRL: (func $10 (; has Stack IR ;) (param $0 f64) (result f64) +;; GENRL-NEXT: (f64.neg +;; GENRL-NEXT: (local.get $0) +;; GENRL-NEXT: ) +;; GENRL-NEXT: ) + +;; GENRL: (func $11 (; has Stack IR ;) (param $0 f32) (result f32) +;; GENRL-NEXT: (f32.abs +;; GENRL-NEXT: (local.get $0) +;; GENRL-NEXT: ) +;; GENRL-NEXT: ) + +;; GENRL: (func $12 (; has Stack IR ;) (param $0 f64) (result f64) +;; GENRL-NEXT: (f64.abs +;; GENRL-NEXT: (local.get $0) +;; GENRL-NEXT: ) +;; GENRL-NEXT: )