From ddd3d396de176bff0fa6890c4fe91a83c3c9f13c Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 19 Oct 2021 15:45:46 -0700 Subject: [PATCH 1/5] start --- src/binaryen-c.cpp | 4 ++-- src/pass.h | 14 +++++++++----- src/passes/OptimizeInstructions.cpp | 18 +++++++++--------- src/tools/optimization-options.h | 6 +++--- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index 0a4b88a2ba0..c6539052249 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -3932,9 +3932,9 @@ 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..ecaa28bc09e 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-relaed 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..eba3fa5831a 100644 --- a/src/tools/optimization-options.h +++ b/src/tools/optimization-options.h @@ -206,11 +206,11 @@ struct OptimizationOptions : public ToolOptions { passOptions.lowMemoryUnused = true; }) .add( - "--fast-math", + "--fast-math-ignore-nans", "-ffm", - "Optimize floats without handling corner cases of NaNs and rounding", + "Optimize floats without handling corner cases of NaNs", Options::Arguments::Zero, - [this](Options*, const std::string&) { passOptions.fastMath = true; }) + [this](Options*, const std::string&) { passOptions.fastMath.ignoreNaNs = true; }) .add("--zero-filled-memory", "-uim", "Assume that an imported memory will be zero-initialized", From 696c88126d92caa980c89d4580bcf510380332e9 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 19 Oct 2021 15:45:55 -0700 Subject: [PATCH 2/5] format --- src/binaryen-c.cpp | 4 +++- src/tools/optimization-options.h | 13 +++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index c6539052249..9d8c156cfeb 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -3934,7 +3934,9 @@ void BinaryenSetZeroFilledMemory(bool on) { bool BinaryenGetFastMath(void) { return globalPassOptions.fastMath.ignoreNaNs; } -void BinaryenSetFastMath(bool value) { globalPassOptions.fastMath.ignoreNaNs = value; } +void BinaryenSetFastMath(bool value) { + globalPassOptions.fastMath.ignoreNaNs = value; +} const char* BinaryenGetPassArgument(const char* key) { assert(key); diff --git a/src/tools/optimization-options.h b/src/tools/optimization-options.h index eba3fa5831a..331329f634d 100644 --- a/src/tools/optimization-options.h +++ b/src/tools/optimization-options.h @@ -205,12 +205,13 @@ struct OptimizationOptions : public ToolOptions { [this](Options*, const std::string&) { passOptions.lowMemoryUnused = true; }) - .add( - "--fast-math-ignore-nans", - "-ffm", - "Optimize floats without handling corner cases of NaNs", - Options::Arguments::Zero, - [this](Options*, const std::string&) { passOptions.fastMath.ignoreNaNs = true; }) + .add("--fast-math-ignore-nans", + "-ffm", + "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", From dbee75158f6c11b71711a7480dbf2441ab498796 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 19 Oct 2021 15:47:21 -0700 Subject: [PATCH 3/5] tests --- src/tools/optimization-options.h | 2 +- test/lit/help/optimization-opts.test | 5 ++--- test/lit/passes/O_fast-math.wast | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/tools/optimization-options.h b/src/tools/optimization-options.h index 331329f634d..3e3a1c927f0 100644 --- a/src/tools/optimization-options.h +++ b/src/tools/optimization-options.h @@ -206,7 +206,7 @@ struct OptimizationOptions : public ToolOptions { passOptions.lowMemoryUnused = true; }) .add("--fast-math-ignore-nans", - "-ffm", + "-fmin", "Optimize floats without handling corner cases of NaNs", Options::Arguments::Zero, [this](Options*, const std::string&) { 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..a7c863432d1 100644 --- a/test/lit/passes/O_fast-math.wast +++ b/test/lit/passes/O_fast-math.wast @@ -1,7 +1,7 @@ ;; 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 ;; with fast-math we can optimize some of these patterns (module From 5e3be86acf61bb0a168d905871c0a41101bff1f9 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 19 Oct 2021 16:06:03 -0700 Subject: [PATCH 4/5] test --- src/tools/optimization-options.h | 8 ++++ test/lit/passes/O_fast-math.wast | 69 ++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/src/tools/optimization-options.h b/src/tools/optimization-options.h index 3e3a1c927f0..d0a3822e1c2 100644 --- a/src/tools/optimization-options.h +++ b/src/tools/optimization-options.h @@ -205,6 +205,14 @@ struct OptimizationOptions : public ToolOptions { [this](Options*, const std::string&) { passOptions.lowMemoryUnused = 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", diff --git a/test/lit/passes/O_fast-math.wast b/test/lit/passes/O_fast-math.wast index a7c863432d1..3e5fa8ebcca 100644 --- a/test/lit/passes/O_fast-math.wast +++ b/test/lit/passes/O_fast-math.wast @@ -2,6 +2,7 @@ ;; NOTE: This test was ported using port_test.py and could be cleaned up. ;; 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: ) From 191f8b3c6f2beef9f33ad3011f5fc170f3ffa226 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 19 Oct 2021 16:07:29 -0700 Subject: [PATCH 5/5] fix --- src/pass.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pass.h b/src/pass.h index ecaa28bc09e..cb641dffcdc 100644 --- a/src/pass.h +++ b/src/pass.h @@ -91,7 +91,7 @@ struct InliningOptions { struct FastMathOptions { // Allow optimizing under the assumption that NaNs don't happen. This lets us - // ignore all the NaN-relaed corner cases. + // ignore all the NaN-related corner cases. bool ignoreNaNs = false; };