summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/pass.h5
-rw-r--r--src/passes/OptimizeInstructions.cpp19
-rw-r--r--src/tools/optimization-options.h8
-rw-r--r--src/wasm/literal.cpp41
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");