diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 49 | ||||
-rw-r--r-- | src/support/bits.cpp | 24 | ||||
-rw-r--r-- | src/support/bits.h | 3 |
3 files changed, 71 insertions, 5 deletions
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 1e51adea9..85ab2a168 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -606,6 +606,18 @@ struct OptimizeInstructions } } } + if (binary->op == DivFloat32) { + float c = right->value.getf32(); + if (Bits::isPowerOf2Float(c)) { + return optimizePowerOf2FDiv(binary, c); + } + } + if (binary->op == DivFloat64) { + double c = right->value.getf64(); + if (Bits::isPowerOf2Float(c)) { + return optimizePowerOf2FDiv(binary, c); + } + } } // a bunch of operations on a constant left side can be simplified if (binary->left->is<Const>()) { @@ -1260,6 +1272,15 @@ private: // but it's still worth doing since // * Usually ands are more common than urems. // * The constant is slightly smaller. + template<typename T> Expression* optimizePowerOf2URem(Binary* binary, T c) { + static_assert(std::is_same<T, uint32_t>::value || + std::is_same<T, uint64_t>::value, + "type mismatch"); + binary->op = std::is_same<T, uint32_t>::value ? AndInt32 : AndInt64; + binary->right->cast<Const>()->value = Literal(c - 1); + return binary; + } + template<typename T> Expression* optimizePowerOf2UDiv(Binary* binary, T c) { static_assert(std::is_same<T, uint32_t>::value || std::is_same<T, uint64_t>::value, @@ -1270,12 +1291,30 @@ private: return binary; } - template<typename T> Expression* optimizePowerOf2URem(Binary* binary, T c) { - static_assert(std::is_same<T, uint32_t>::value || - std::is_same<T, uint64_t>::value, + template<typename T> Expression* optimizePowerOf2FDiv(Binary* binary, T c) { + // + // x / C_pot => x * (C_pot ^ -1) + // + // Explanation: + // Floating point numbers are represented as: + // ((-1) ^ sign) * (2 ^ (exp - bias)) * (1 + significand) + // + // If we have power of two numbers, then the mantissa (significand) + // is all zeros. Let's focus on the exponent, ignoring the sign part: + // (2 ^ (exp - bias)) + // + // and for inverted power of two floating point: + // 1.0 / (2 ^ (exp - bias)) -> 2 ^ -(exp - bias) + // + // So inversion of C_pot is valid because it changes only the sign + // of the exponent part and doesn't touch the significand part, + // which remains the same (zeros). + static_assert(std::is_same<T, float>::value || + std::is_same<T, double>::value, "type mismatch"); - binary->op = std::is_same<T, uint32_t>::value ? AndInt32 : AndInt64; - binary->right->cast<Const>()->value = Literal(c - 1); + double invDivisor = 1.0 / (double)c; + binary->op = std::is_same<T, float>::value ? MulFloat32 : MulFloat64; + binary->right->cast<Const>()->value = Literal(static_cast<T>(invDivisor)); return binary; } diff --git a/src/support/bits.cpp b/src/support/bits.cpp index 03fb66252..8a81a7e83 100644 --- a/src/support/bits.cpp +++ b/src/support/bits.cpp @@ -157,6 +157,30 @@ int ceilLog2(uint32_t v) { return 32 - countLeadingZeroes(v - 1); } int ceilLog2(uint64_t v) { return 64 - countLeadingZeroes(v - 1); } +bool isPowerOf2Float(float v) { + // Power of two floating points should have zero as their significands, + // so here we just mask the exponent range of "v" and compare it with the + // unmasked input value. If they are equal, our value is a power of + // two. Also, we reject all values which are less than the minimal possible + // power of two or greater than the maximum possible power of two. + const uint32_t MIN_POT = 0x01U << 23; // 0x1p-126 + const uint32_t MAX_POT = 0xFDU << 23; // 0x1p+126 + const uint32_t EXP_MASK = 0xFFU << 23; // mask only exponent + const uint32_t SIGN_MASK = ~0U >> 1; // mask everything except sign + auto u = bit_cast<uint32_t>(v) & SIGN_MASK; + return u >= MIN_POT && u <= MAX_POT && (u & EXP_MASK) == u; +} + +bool isPowerOf2Float(double v) { + // See isPowerOf2Float(float) + const uint64_t MIN_POT = 0x001ULL << 52; // 0x1p-1022 + const uint64_t MAX_POT = 0x7FDULL << 52; // 0x1p+1022 + const uint64_t EXP_MASK = 0x7FFULL << 52; // mask only exponent + const uint64_t SIGN_MASK = ~0ULL >> 1; // mask everything except sign + auto u = bit_cast<uint64_t>(v) & SIGN_MASK; + return u >= MIN_POT && u <= MAX_POT && (u & EXP_MASK) == u; +} + uint32_t log2(uint32_t v) { if (!isPowerOf2(v)) { WASM_UNREACHABLE("value should be a power of two"); diff --git a/src/support/bits.h b/src/support/bits.h index 3231e1c96..333eb5cc2 100644 --- a/src/support/bits.h +++ b/src/support/bits.h @@ -77,6 +77,9 @@ template<typename T> bool isPowerOf2(T v) { return v != 0 && (v & (v - 1)) == 0; } +bool isPowerOf2Float(float); +bool isPowerOf2Float(double); + template<typename T, typename U> inline static T rotateLeft(T val, U count) { auto value = typename std::make_unsigned<T>::type(val); U mask = sizeof(T) * CHAR_BIT - 1; |