Skip to content
Open
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
6 changes: 4 additions & 2 deletions src/binaryen-c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
14 changes: 9 additions & 5 deletions src/pass.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
18 changes: 9 additions & 9 deletions src/passes/OptimizeInstructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
{
Expand Down Expand Up @@ -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<Const>()) {
if (c->value.isZero()) {
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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
Expand All @@ -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;
}
}
Expand Down
21 changes: 15 additions & 6 deletions src/tools/optimization-options.h
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 2 additions & 3 deletions test/lit/help/optimization-opts.test
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
71 changes: 70 additions & 1 deletion test/lit/passes/O_fast-math.wast
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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: )