diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/pass.h | 5 | ||||
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 19 | ||||
-rw-r--r-- | src/tools/optimization-options.h | 8 | ||||
-rw-r--r-- | src/wasm/literal.cpp | 41 |
4 files changed, 27 insertions, 46 deletions
diff --git a/src/pass.h b/src/pass.h index a3ee41d61..27e7ee37f 100644 --- a/src/pass.h +++ b/src/pass.h @@ -102,6 +102,11 @@ 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 try to preserve debug info through, which are special calls. bool debugInfo = false; // Arbitrary string arguments from the commandline, which we forward to diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 36d92e81f..55af9a34d 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -161,7 +161,10 @@ struct OptimizeInstructions #endif } + bool fastMath; + void doWalkFunction(Function* func) { + fastMath = getPassOptions().fastMath; // first, scan locals { LocalScanner scanner(localInfo, getPassOptions()); @@ -1414,14 +1417,15 @@ private: } { double value; - if (matches(curr, binary(Abstract::Sub, any(), fval(&value))) && + if (fastMath && + matches(curr, binary(Abstract::Sub, any(), fval(&value))) && value == 0.0) { // x - (-0.0) ==> x + 0.0 if (std::signbit(value)) { curr->op = Abstract::getBinary(type, Abstract::Add); right->value = right->value.neg(); return curr; - } else { + } else if (fastMath) { // x - 0.0 ==> x return curr->left; } @@ -1430,19 +1434,18 @@ private: { // x + (-0.0) ==> x double value; - if (matches(curr, binary(Abstract::Add, any(), fval(&value))) && + if (fastMath && + matches(curr, binary(Abstract::Add, any(), fval(&value))) && value == 0.0 && std::signbit(value)) { return curr->left; } } - // Note that this is correct even on floats with a NaN on the left, - // as a NaN would skip the computation and just return the NaN, - // and that is precisely what we do here. but, the same with -1 - // (change to a negation) would be incorrect for that reason. if (matches(curr, binary(Abstract::Mul, any(&left), constant(1))) || matches(curr, binary(Abstract::DivS, any(&left), constant(1))) || matches(curr, binary(Abstract::DivU, any(&left), constant(1)))) { - return left; + if (curr->type.isInteger() || fastMath) { + return left; + } } return nullptr; } diff --git a/src/tools/optimization-options.h b/src/tools/optimization-options.h index 72f478329..5b6a643e6 100644 --- a/src/tools/optimization-options.h +++ b/src/tools/optimization-options.h @@ -187,7 +187,13 @@ struct OptimizationOptions : public ToolOptions { Options::Arguments::Zero, [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 passes in registry for (const auto& p : PassRegistry::get()->getRegisteredNames()) { (*this).add( diff --git a/src/wasm/literal.cpp b/src/wasm/literal.cpp index d309be308..54453b356 100644 --- a/src/wasm/literal.cpp +++ b/src/wasm/literal.cpp @@ -934,35 +934,10 @@ Literal Literal::mul(const Literal& other) const { return Literal(uint32_t(i32) * uint32_t(other.i32)); case Type::i64: return Literal(uint64_t(i64) * uint64_t(other.i64)); - case Type::f32: { - // Special-case multiplication by 1. nan * 1 can change nan bits per the - // wasm spec, but it is ok to just return that original nan, and we - // do that here so that we are consistent with the optimization of - // removing the * 1 and leaving just the nan. That is, if we just - // do a normal multiply and the CPU decides to change the bits, we'd - // give a different result on optimized code, which would look like - // it was a bad optimization. So out of all the valid results to - // return here, return the simplest one that is consistent with - // our optimization for the case of 1. - float lhs = getf32(), rhs = other.getf32(); - if (rhs == 1) { - return Literal(lhs); - } - if (lhs == 1) { - return Literal(rhs); - } - return Literal(lhs * rhs); - } - case Type::f64: { - double lhs = getf64(), rhs = other.getf64(); - if (rhs == 1) { - return Literal(lhs); - } - if (lhs == 1) { - return Literal(rhs); - } - return Literal(lhs * rhs); - } + case Type::f32: + return Literal(getf32() * other.getf32()); + case Type::f64: + return Literal(getf64() * other.getf64()); case Type::v128: case Type::funcref: case Type::externref: @@ -1002,10 +977,6 @@ Literal Literal::div(const Literal& other) const { case FP_INFINITE: // fallthrough case FP_NORMAL: // fallthrough case FP_SUBNORMAL: - // Special-case division by 1, similar to multiply from earlier. - if (rhs == 1) { - return Literal(lhs); - } return Literal(lhs / rhs); default: WASM_UNREACHABLE("invalid fp classification"); @@ -1034,10 +1005,6 @@ Literal Literal::div(const Literal& other) const { case FP_INFINITE: // fallthrough case FP_NORMAL: // fallthrough case FP_SUBNORMAL: - // See above comment on f32. - if (rhs == 1) { - return Literal(lhs); - } return Literal(lhs / rhs); default: WASM_UNREACHABLE("invalid fp classification"); |