summaryrefslogtreecommitdiff
path: root/src/passes/OptimizeInstructions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/passes/OptimizeInstructions.cpp')
-rw-r--r--src/passes/OptimizeInstructions.cpp49
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;
}