diff options
Diffstat (limited to 'src/passes/OptimizeInstructions.cpp')
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 49 |
1 files changed, 44 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; } |