Modified: trunk/Source/_javascript_Core/b3/B3ReduceDoubleToFloat.cpp (199647 => 199648)
--- trunk/Source/_javascript_Core/b3/B3ReduceDoubleToFloat.cpp 2016-04-17 21:53:11 UTC (rev 199647)
+++ trunk/Source/_javascript_Core/b3/B3ReduceDoubleToFloat.cpp 2016-04-18 05:13:13 UTC (rev 199648)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -29,128 +29,456 @@
#if ENABLE(B3_JIT)
#include "B3BasicBlock.h"
+#include "B3IndexSet.h"
+#include "B3InsertionSetInlines.h"
#include "B3PhaseScope.h"
+#include "B3UseCounts.h"
#include "B3ValueInlines.h"
namespace JSC { namespace B3 {
namespace {
-void attemptSimplification(Value* candidate)
-{
- switch (candidate->opcode()) {
- case Add:
- case Sub:
- case Mul:
- case Div:
- if (candidate->child(0)->opcode() == FloatToDouble && candidate->child(1)->opcode() == FloatToDouble) {
- candidate->child(0) = candidate->child(0)->child(0);
- candidate->child(1) = candidate->child(1)->child(0);
- candidate->setType(Float);
+
+bool verbose = false;
+bool printRemainingConversions = false;
+
+class DoubleToFloatReduction {
+public:
+ DoubleToFloatReduction(Procedure& procedure)
+ : m_procedure(procedure)
+ {
+ }
+
+ void run()
+ {
+ if (!findCandidates())
+ return;
+
+ findPhisContainingFloat();
+
+ simplify();
+
+ cleanUp();
+ }
+
+private:
+ // This step find values that are used as Double and cannot be converted to Float..
+ // It flows the information backward through Phi-Upsilons.
+ bool findCandidates()
+ {
+ bool foundConversionCandidate = false;
+ Vector<Value*, 32> upsilons;
+
+ // First, we find all values that are strictly used as double.
+ // Those are values used by something else than DoubleToFloat.
+ //
+ // We don't know the state of Upsilons until their Phi has been
+ // set. We just keep a list of them and update them next.
+ for (BasicBlock* block : m_procedure) {
+ for (Value* value : *block) {
+ value->performSubstitution();
+
+ if (value->opcode() == DoubleToFloat) {
+ foundConversionCandidate = true;
+
+ Value* child = value->child(0);
+ if (child->opcode() == FloatToDouble) {
+ // We don't really need to simplify this early but it simplifies debugging.
+ value->replaceWithIdentity(child->child(0));
+ }
+ continue;
+ }
+
+ if (value->opcode() == FloatToDouble)
+ foundConversionCandidate = true;
+
+ if (value->opcode() == Upsilon) {
+ Value* child = value->child(0);
+ if (child->type() == Double)
+ upsilons.append(value);
+ continue;
+ }
+
+ for (Value* child : value->children()) {
+ if (child->type() == Double)
+ m_valuesUsedAsDouble.add(child);
+ }
+ }
}
- break;
- case Abs:
- case Ceil:
- case Floor:
- case Sqrt:
- if (candidate->child(0)->opcode() == FloatToDouble) {
- candidate->child(0) = candidate->child(0)->child(0);
- candidate->setType(Float);
+
+ if (!foundConversionCandidate)
+ return false;
+
+ // Now we just need to propagate through Phi-Upsilon.
+ // A Upsilon can convert its input to float if its phi is never used as double.
+ // If we modify a phi, we need to continue until all the Upsilon-Phi converge.
+ bool changedPhiState;
+ do {
+ changedPhiState = false;
+ for (Value* value : upsilons) {
+ UpsilonValue* upsilon = value->as<UpsilonValue>();
+ Value* phi = upsilon->phi();
+ if (!m_valuesUsedAsDouble.contains(phi))
+ continue;
+
+ Value* child = value->child(0);
+ bool childChanged = m_valuesUsedAsDouble.add(child);
+ if (childChanged && child->opcode() == Phi)
+ changedPhiState = true;
+ }
+ } while (changedPhiState);
+
+ if (verbose) {
+ dataLog("Conversion candidates:\n");
+ for (BasicBlock* block : m_procedure) {
+ for (Value* value : *block) {
+ if (value->type() == Double && !m_valuesUsedAsDouble.contains(value))
+ dataLog(" ", deepDump(m_procedure, value), "\n");
+ }
+ }
+ dataLog("\n");
}
- break;
- default:
- break;
+
+ return true;
}
-}
-} // namespace
+ // This step finds Phis of type Double that effectively contains Float values.
+ // It flows that information forward through Phi-Upsilons.
+ void findPhisContainingFloat()
+ {
+ Vector<Value*, 32> upsilons;
-// The goal of this phase is to transform Double operations
-// into float if the Double result is never used as Double.
-//
-// In C, that would be something like:
-// float add(float a, float b) {
-// return (double)a + (double)b;
-// }
-//
-// Such operation arise in JS because there are is no Float type
-// and float operations are generated by adding double-to-float rounding.
-//
-// The operations can be done entirely without Double conversion.
-// Using float in place remove the useless conversion, and the float
-// ops is sometime massively cheaper (SQRT for example).
-//
-// If the Double value is used as Double, we do not do the conversion.
-// It is cheaper to do a conversion than repeat any floating-point operation.
-void reduceDoubleToFloat(Procedure& procedure)
-{
- // FIXME: We should tune this phase for performance and make it part of ReduceStrength.
- // ReduceStrength can eliminate nodes that prevents us from simplifying operations.
- PhaseScope phaseScope(procedure, "reduceDoubleToFloat");
+ // The Double value that can be safely turned into a Float are:
+ // - FloatToDouble
+ // - ConstDouble with a value that converts to Float without losing precision.
+ for (BasicBlock* block : m_procedure) {
+ for (Value* value : *block) {
+ if (value->opcode() != Upsilon)
+ continue;
- HashSet<Value*> candidates;
+ Value* child = value->child(0);
+ if (child->type() != Double
+ || child->opcode() == FloatToDouble)
+ continue;
- // First, we find any value that is converted to float
- // and only used as float.
- // We also simplify comparisons since that's always safe and we
- // don't want them to appear in the next loop.
- for (BasicBlock* block : procedure) {
- for (Value* value : *block) {
- value->performSubstitution();
+ if (child->hasDouble()) {
+ double constValue = child->asDouble();
+ if (isIdentical(static_cast<double>(static_cast<float>(constValue)), constValue))
+ continue;
+ }
- switch (value->opcode()) {
- case DoubleToFloat:
- candidates.add(value->child(0));
- break;
- case Equal:
- case NotEqual:
- case LessThan:
- case GreaterThan:
- case LessEqual:
- case GreaterEqual:
- case EqualOrUnordered:
- if (value->child(0)->opcode() == FloatToDouble && value->child(1)->opcode() == FloatToDouble) {
- value->child(0) = value->child(0)->child(0);
- value->child(1) = value->child(1)->child(0);
+ if (child->opcode() == Phi) {
+ upsilons.append(value);
+ continue;
}
- break;
- default:
- break;
+
+ UpsilonValue* upsilon = value->as<UpsilonValue>();
+ Value* phi = upsilon->phi();
+ m_phisContainingDouble.add(phi);
}
}
+
+ // Propagate the flags forward.
+ bool changedPhiState;
+ do {
+ changedPhiState = false;
+ for (Value* value : upsilons) {
+ Value* child = value->child(0);
+ if (m_phisContainingDouble.contains(child)) {
+ UpsilonValue* upsilon = value->as<UpsilonValue>();
+ Value* phi = upsilon->phi();
+ changedPhiState |= m_phisContainingDouble.add(phi);
+ }
+ }
+ } while (changedPhiState);
+
+ if (verbose) {
+ dataLog("Phis containing float values:\n");
+ for (BasicBlock* block : m_procedure) {
+ for (Value* value : *block) {
+ if (value->opcode() == Phi
+ && value->type() == Double
+ && !m_phisContainingDouble.contains(value))
+ dataLog(" ", deepDump(m_procedure, value), "\n");
+ }
+ }
+ dataLog("\n");
+ }
}
- for (BasicBlock* block : procedure) {
- for (Value* value : *block) {
- if (value->opcode() == DoubleToFloat)
- continue;
+ bool canBeTransformedToFloat(Value* value)
+ {
+ if (value->opcode() == FloatToDouble)
+ return true;
- for (Value* child : value->children()) {
- if (child->type() == Double)
- candidates.remove(child);
+ if (value->hasDouble())
+ return true; // Double constant truncated to float.
+
+ if (value->opcode() == Phi) {
+ return value->type() == Float
+ || (value->type() == Double && !m_phisContainingDouble.contains(value));
+ }
+ return false;
+ }
+
+ Value* transformToFloat(Value* value, unsigned valueIndex, InsertionSet& insertionSet)
+ {
+ ASSERT(canBeTransformedToFloat(value));
+ if (value->opcode() == FloatToDouble)
+ return value->child(0);
+
+ if (value->hasDouble())
+ return insertionSet.insert<ConstFloatValue>(valueIndex, value->origin(), static_cast<float>(value->asDouble()));
+
+ if (value->opcode() == Phi) {
+ convertPhi(value);
+ return value;
+ }
+ RELEASE_ASSERT_NOT_REACHED();
+ return nullptr;
+ }
+
+ void convertPhi(Value* phi)
+ {
+ ASSERT(phi->opcode() == Phi);
+ phi->setType(Float);
+ m_convertedPhis.add(phi);
+ }
+
+ bool attemptTwoOperandsSimplify(Value* candidate, unsigned candidateIndex, InsertionSet& insertionSet)
+ {
+ Value* left = candidate->child(0);
+ Value* right = candidate->child(1);
+ if (!canBeTransformedToFloat(left) || !canBeTransformedToFloat(right))
+ return false;
+
+ m_convertedValue.add(candidate);
+ candidate->child(0) = transformToFloat(left, candidateIndex, insertionSet);
+ candidate->child(1) = transformToFloat(right, candidateIndex, insertionSet);
+ return true;
+ }
+
+ // Simplify Double operations into Float operations.
+ void simplify()
+ {
+ Vector<Value*, 32> upsilonReferencingDoublePhi;
+
+ InsertionSet insertionSet(m_procedure);
+ for (BasicBlock* block : m_procedure) {
+ for (unsigned index = 0; index < block->size(); ++index) {
+ Value* value = block->at(index);
+
+ switch (value->opcode()) {
+ case Equal:
+ case NotEqual:
+ case LessThan:
+ case GreaterThan:
+ case LessEqual:
+ case GreaterEqual:
+ case EqualOrUnordered:
+ attemptTwoOperandsSimplify(value, index, insertionSet);
+ continue;
+ case Upsilon: {
+ Value* child = value->child(0);
+ if (child->opcode() == Phi && child->type() == Double)
+ upsilonReferencingDoublePhi.append(value);
+ continue;
+ }
+ default:
+ break;
+ }
+
+ if (m_valuesUsedAsDouble.contains(value))
+ continue;
+
+ switch (value->opcode()) {
+ case Add:
+ case Sub:
+ case Mul:
+ case Div:
+ if (attemptTwoOperandsSimplify(value, index, insertionSet))
+ value->setType(Float);
+ break;
+ case Abs:
+ case Ceil:
+ case Floor:
+ case Sqrt: {
+ Value* child = value->child(0);
+ if (canBeTransformedToFloat(child)) {
+ value->child(0) = transformToFloat(child, index, insertionSet);
+ value->setType(Float);
+ m_convertedValue.add(value);
+ }
+ break;
+ }
+ case FloatToDouble:
+ // This happens if we round twice.
+ // Typically, this is indirect through Phi-Upsilons.
+ // The Upsilon rounds and the Phi rounds.
+ value->setType(Float);
+ value->replaceWithIdentity(value->child(0));
+ m_convertedValue.add(value);
+ break;
+ case Phi:
+ // If a Phi is always converted to Float, we always make it into a float Phi-Upsilon.
+ // This is a simplistic view of things. Ideally we should keep type that will minimize
+ // the amount of conversion in the loop.
+ if (value->type() == Double)
+ convertPhi(value);
+ break;
+ default:
+ break;
+ }
}
+ insertionSet.execute(block);
}
+
+ if (!upsilonReferencingDoublePhi.isEmpty()) {
+ // If a Phi contains Float values typed as Double, but is not used as Float
+ // by a non-trivial operation, we did not convert it.
+ //
+ // We fix that now by converting the remaining phis that contains
+ // float but where not converted to float.
+ bool changedPhi;
+ do {
+ changedPhi = false;
+
+ for (Value* value : upsilonReferencingDoublePhi) {
+ UpsilonValue* upsilon = value->as<UpsilonValue>();
+ Value* child = value->child(0);
+ Value* phi = upsilon->phi();
+ if (phi->type() == Float && child->type() == Double
+ && !m_phisContainingDouble.contains(child)) {
+ convertPhi(child);
+ changedPhi = true;
+ }
+ }
+
+ } while (changedPhi);
+ }
}
- if (candidates.isEmpty())
+ // We are in an inconsistent state where we have
+ // DoubleToFloat nodes over values producing float and Phis that are
+ // float for Upsilons that are Double.
+ //
+ // This steps puts us back in a consistent state.
+ void cleanUp()
+ {
+ InsertionSet insertionSet(m_procedure);
+
+ for (BasicBlock* block : m_procedure) {
+ for (unsigned index = 0; index < block->size(); ++index) {
+ Value* value = block->at(index);
+ if (value->opcode() == DoubleToFloat && value->child(0)->type() == Float) {
+ value->replaceWithIdentity(value->child(0));
+ continue;
+ }
+
+ if (value->opcode() == Upsilon) {
+ UpsilonValue* upsilon = value->as<UpsilonValue>();
+ Value* child = value->child(0);
+ Value* phi = upsilon->phi();
+
+ if (phi->type() == Float) {
+ if (child->type() == Double) {
+ Value* newChild = nullptr;
+ if (child->opcode() == FloatToDouble)
+ newChild = child->child(0);
+ else if (child->hasDouble())
+ newChild = insertionSet.insert<ConstFloatValue>(index, child->origin(), static_cast<float>(child->asDouble()));
+ else
+ newChild = insertionSet.insert<Value>(index, DoubleToFloat, upsilon->origin(), child);
+ upsilon->child(0) = newChild;
+ }
+ continue;
+ }
+ }
+
+ if (!m_convertedValue.contains(value)) {
+ // Phis can be converted from Double to Float if the value they contain
+ // is not more precise than a Float.
+ // If the value is needed as Double, it has to be converted back.
+ for (Value*& child : value->children()) {
+ if (m_convertedPhis.contains(child))
+ child = insertionSet.insert<Value>(index, FloatToDouble, value->origin(), child);
+ }
+ }
+ }
+ insertionSet.execute(block);
+ }
+ }
+
+ Procedure& m_procedure;
+
+ // Set of all the Double values that are actually used as Double.
+ // Converting any of them to Float would lose precision.
+ IndexSet<Value> m_valuesUsedAsDouble;
+
+ // Set of all the Phi of type Double that really contains a Double.
+ // Any Double Phi not in the set can be converted to Float without losing precision.
+ IndexSet<Value> m_phisContainingDouble;
+
+ // Any value that was converted from producing a Double to producing a Float.
+ // This set does not include Phi-Upsilons.
+ IndexSet<Value> m_convertedValue;
+
+ // Any value that previously produced Double and now produce Float.
+ IndexSet<Value> m_convertedPhis;
+};
+
+void printGraphIfConverting(Procedure& procedure)
+{
+ if (!printRemainingConversions)
return;
- // Second, we go over the candidates and attempt to simplify them.
- // This leaves the graph in an invalid state where Float Values are
- // used by DoubleToFloat Values. This is only temporary.
- for (Value* candidate : candidates)
- attemptSimplification(candidate);
+ UseCounts useCount(procedure);
- // Finally, remove the DoubleToFloat made useless by the simplifications.
+ Vector<Value*> doubleToFloat;
+ Vector<Value*> floatToDouble;
+
for (BasicBlock* block : procedure) {
for (Value* value : *block) {
- if (value->opcode() == DoubleToFloat && value->child(0)->type() == Float)
- value->replaceWithIdentity(value->child(0));
+ if (!useCount.numUses(value))
+ continue;
+
+ if (value->opcode() == DoubleToFloat)
+ doubleToFloat.append(value);
+ if (value->opcode() == FloatToDouble)
+ floatToDouble.append(value);
}
}
- // We do not clean all the useless nodes and conversions. ReduceStrength does that better.
+ if (doubleToFloat.isEmpty() && floatToDouble.isEmpty())
+ return;
+
+ dataLog("Procedure with Float-Double conversion:\n", procedure, "\n");
+ dataLog("Converting nodes:\n");
+ for (Value* value : doubleToFloat)
+ dataLog(" ", deepDump(procedure, value), "\n");
+ for (Value* value : floatToDouble)
+ dataLog(" ", deepDump(procedure, value), "\n");
+
}
+} // anonymous namespace.
+
+void reduceDoubleToFloat(Procedure& procedure)
+{
+ PhaseScope phaseScope(procedure, "reduceDoubleToFloat");
+
+ if (verbose)
+ dataLog("Before DoubleToFloatReduction:\n", procedure, "\n");
+
+ DoubleToFloatReduction doubleToFloatReduction(procedure);
+ doubleToFloatReduction.run();
+
+ if (verbose)
+ dataLog("After DoubleToFloatReduction:\n", procedure, "\n");
+
+ printGraphIfConverting(procedure);
+}
+
} } // namespace JSC::B3
#endif // ENABLE(B3_JIT)
Modified: trunk/Source/_javascript_Core/b3/testb3.cpp (199647 => 199648)
--- trunk/Source/_javascript_Core/b3/testb3.cpp 2016-04-17 21:53:11 UTC (rev 199647)
+++ trunk/Source/_javascript_Core/b3/testb3.cpp 2016-04-18 05:13:13 UTC (rev 199648)
@@ -4153,6 +4153,265 @@
CHECK(isIdentical(effect, sqrt(a)));
}
+void testCompareTwoFloatToDouble(float a, float b)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* arg1As32 = root->appendNew<Value>(proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+ Value* arg1Float = root->appendNew<Value>(proc, BitwiseCast, Origin(), arg1As32);
+ Value* arg1AsDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), arg1Float);
+
+ Value* arg2As32 = root->appendNew<Value>(proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* arg2Float = root->appendNew<Value>(proc, BitwiseCast, Origin(), arg2As32);
+ Value* arg2AsDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), arg2Float);
+ Value* equal = root->appendNew<Value>(proc, Equal, Origin(), arg1AsDouble, arg2AsDouble);
+
+ root->appendNew<ControlValue>(proc, Return, Origin(), equal);
+
+ CHECK(compileAndRun<int64_t>(proc, bitwise_cast<int32_t>(a), bitwise_cast<int32_t>(b)) == (a == b));
+}
+
+void testCompareOneFloatToDouble(float a, double b)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+
+ Value* arg1As32 = root->appendNew<Value>(proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+ Value* arg1Float = root->appendNew<Value>(proc, BitwiseCast, Origin(), arg1As32);
+ Value* arg1AsDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), arg1Float);
+
+ Value* arg2AsDouble = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
+ Value* equal = root->appendNew<Value>(proc, Equal, Origin(), arg1AsDouble, arg2AsDouble);
+
+ root->appendNew<ControlValue>(proc, Return, Origin(), equal);
+
+ CHECK(compileAndRun<int64_t>(proc, bitwise_cast<int32_t>(a), b) == (a == b));
+}
+
+void testCompareFloatToDoubleThroughPhi(float a, float b)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+ BasicBlock* tail = proc.addBlock();
+
+ Value* condition = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+
+ Value* arg1As32 = root->appendNew<Value>(proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* arg1Float = root->appendNew<Value>(proc, BitwiseCast, Origin(), arg1As32);
+ Value* arg1AsDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), arg1Float);
+
+ Value* arg2AsDouble = root->appendNew<ArgumentRegValue>(proc, Origin(), FPRInfo::argumentFPR0);
+ Value* arg2AsFloat = root->appendNew<Value>(proc, DoubleToFloat, Origin(), arg2AsDouble);
+ Value* arg2AsFRoundedDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), arg2AsFloat);
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ condition,
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ UpsilonValue* thenValue = thenCase->appendNew<UpsilonValue>(proc, Origin(), arg1AsDouble);
+ thenCase->appendNew<ControlValue>(proc, Jump, Origin(), FrequentedBlock(tail));
+
+ Value* elseConst = elseCase->appendNew<ConstDoubleValue>(proc, Origin(), 0.);
+ UpsilonValue* elseValue = elseCase->appendNew<UpsilonValue>(proc, Origin(), elseConst);
+ elseCase->appendNew<ControlValue>(proc, Jump, Origin(), FrequentedBlock(tail));
+
+ Value* doubleInput = tail->appendNew<Value>(proc, Phi, Double, Origin());
+ thenValue->setPhi(doubleInput);
+ elseValue->setPhi(doubleInput);
+ Value* equal = tail->appendNew<Value>(proc, Equal, Origin(), doubleInput, arg2AsFRoundedDouble);
+ tail->appendNew<ControlValue>(proc, Return, Origin(), equal);
+
+ auto code = compile(proc);
+ int32_t integerA = bitwise_cast<int32_t>(a);
+ double doubleB = b;
+ CHECK(invoke<int64_t>(*code, 1, integerA, doubleB) == (a == b));
+ CHECK(invoke<int64_t>(*code, 0, integerA, doubleB) == (b == 0));
+}
+
+void testDoubleToFloatThroughPhi(float value)
+{
+ // Simple case of:
+ // if (a) {
+ // x = DoubleAdd(a, b)
+ // else
+ // x = DoubleAdd(a, c)
+ // DoubleToFloat(x)
+ //
+ // Both Adds can be converted to float add.
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+ BasicBlock* tail = proc.addBlock();
+
+ Value* condition = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
+ Value* argAsDouble = root->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ condition,
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ Value* postitiveConst = thenCase->appendNew<ConstDoubleValue>(proc, Origin(), 42.5f);
+ Value* thenAdd = thenCase->appendNew<Value>(proc, Add, Origin(), argAsDouble, postitiveConst);
+ UpsilonValue* thenValue = thenCase->appendNew<UpsilonValue>(proc, Origin(), thenAdd);
+ thenCase->appendNew<ControlValue>(proc, Jump, Origin(), FrequentedBlock(tail));
+
+ Value* elseConst = elseCase->appendNew<ConstDoubleValue>(proc, Origin(), M_PI);
+ UpsilonValue* elseValue = elseCase->appendNew<UpsilonValue>(proc, Origin(), elseConst);
+ elseCase->appendNew<ControlValue>(proc, Jump, Origin(), FrequentedBlock(tail));
+
+ Value* doubleInput = tail->appendNew<Value>(proc, Phi, Double, Origin());
+ thenValue->setPhi(doubleInput);
+ elseValue->setPhi(doubleInput);
+ Value* floatResult = tail->appendNew<Value>(proc, DoubleToFloat, Origin(), doubleInput);
+ tail->appendNew<ControlValue>(proc, Return, Origin(), floatResult);
+
+ auto code = compile(proc);
+ CHECK(isIdentical(invoke<float>(*code, 1, bitwise_cast<int32_t>(value)), value + 42.5f));
+ CHECK(isIdentical(invoke<float>(*code, 0, bitwise_cast<int32_t>(value)), static_cast<float>(M_PI)));
+}
+
+void testDoubleProducerPhiToFloatConversion(float value)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+ BasicBlock* tail = proc.addBlock();
+
+ Value* condition = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ condition,
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ Value* asDouble = thenCase->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
+ UpsilonValue* thenValue = thenCase->appendNew<UpsilonValue>(proc, Origin(), asDouble);
+ thenCase->appendNew<ControlValue>(proc, Jump, Origin(), FrequentedBlock(tail));
+
+ Value* constDouble = elseCase->appendNew<ConstDoubleValue>(proc, Origin(), 42.5);
+ UpsilonValue* elseValue = elseCase->appendNew<UpsilonValue>(proc, Origin(), constDouble);
+ elseCase->appendNew<ControlValue>(proc, Jump, Origin(), FrequentedBlock(tail));
+
+ Value* doubleInput = tail->appendNew<Value>(proc, Phi, Double, Origin());
+ thenValue->setPhi(doubleInput);
+ elseValue->setPhi(doubleInput);
+
+ Value* argAsDoubleAgain = tail->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
+ Value* finalAdd = tail->appendNew<Value>(proc, Add, Origin(), doubleInput, argAsDoubleAgain);
+ Value* floatResult = tail->appendNew<Value>(proc, DoubleToFloat, Origin(), finalAdd);
+ tail->appendNew<ControlValue>(proc, Return, Origin(), floatResult);
+
+ auto code = compile(proc);
+ CHECK(isIdentical(invoke<float>(*code, 1, bitwise_cast<int32_t>(value)), value + value));
+ CHECK(isIdentical(invoke<float>(*code, 0, bitwise_cast<int32_t>(value)), 42.5f + value));
+}
+
+void testDoubleProducerPhiToFloatConversionWithDoubleConsumer(float value)
+{
+ // In this case, the Upsilon-Phi effectively contains a Float value, but it is used
+ // as a Float and as a Double.
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+ BasicBlock* tail = proc.addBlock();
+
+ Value* condition = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ condition,
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ Value* asDouble = thenCase->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
+ UpsilonValue* thenValue = thenCase->appendNew<UpsilonValue>(proc, Origin(), asDouble);
+ thenCase->appendNew<ControlValue>(proc, Jump, Origin(), FrequentedBlock(tail));
+
+ Value* constDouble = elseCase->appendNew<ConstDoubleValue>(proc, Origin(), 42.5);
+ UpsilonValue* elseValue = elseCase->appendNew<UpsilonValue>(proc, Origin(), constDouble);
+ elseCase->appendNew<ControlValue>(proc, Jump, Origin(), FrequentedBlock(tail));
+
+ Value* doubleInput = tail->appendNew<Value>(proc, Phi, Double, Origin());
+ thenValue->setPhi(doubleInput);
+ elseValue->setPhi(doubleInput);
+
+ Value* argAsDoubleAgain = tail->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
+ Value* floatAdd = tail->appendNew<Value>(proc, Add, Origin(), doubleInput, argAsDoubleAgain);
+
+ // FRound.
+ Value* floatResult = tail->appendNew<Value>(proc, DoubleToFloat, Origin(), floatAdd);
+ Value* doubleResult = tail->appendNew<Value>(proc, FloatToDouble, Origin(), floatResult);
+
+ // This one *cannot* be eliminated
+ Value* doubleAdd = tail->appendNew<Value>(proc, Add, Origin(), doubleInput, doubleResult);
+
+ tail->appendNew<ControlValue>(proc, Return, Origin(), doubleAdd);
+
+ auto code = compile(proc);
+ CHECK(isIdentical(invoke<double>(*code, 1, bitwise_cast<int32_t>(value)), (value + value) + static_cast<double>(value)));
+ CHECK(isIdentical(invoke<double>(*code, 0, bitwise_cast<int32_t>(value)), (42.5f + value) + 42.5f));
+}
+
+void testDoubleProducerPhiWithNonFloatConst(float value, double constValue)
+{
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ BasicBlock* thenCase = proc.addBlock();
+ BasicBlock* elseCase = proc.addBlock();
+ BasicBlock* tail = proc.addBlock();
+
+ Value* condition = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+ Value* argument32 = root->appendNew<Value>(proc, Trunc, Origin(),
+ root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR1));
+ Value* floatValue = root->appendNew<Value>(proc, BitwiseCast, Origin(), argument32);
+
+ root->appendNew<ControlValue>(
+ proc, Branch, Origin(),
+ condition,
+ FrequentedBlock(thenCase), FrequentedBlock(elseCase));
+
+ Value* asDouble = thenCase->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
+ UpsilonValue* thenValue = thenCase->appendNew<UpsilonValue>(proc, Origin(), asDouble);
+ thenCase->appendNew<ControlValue>(proc, Jump, Origin(), FrequentedBlock(tail));
+
+ Value* constDouble = elseCase->appendNew<ConstDoubleValue>(proc, Origin(), constValue);
+ UpsilonValue* elseValue = elseCase->appendNew<UpsilonValue>(proc, Origin(), constDouble);
+ elseCase->appendNew<ControlValue>(proc, Jump, Origin(), FrequentedBlock(tail));
+
+ Value* doubleInput = tail->appendNew<Value>(proc, Phi, Double, Origin());
+ thenValue->setPhi(doubleInput);
+ elseValue->setPhi(doubleInput);
+
+ Value* argAsDoubleAgain = tail->appendNew<Value>(proc, FloatToDouble, Origin(), floatValue);
+ Value* finalAdd = tail->appendNew<Value>(proc, Add, Origin(), doubleInput, argAsDoubleAgain);
+ Value* floatResult = tail->appendNew<Value>(proc, DoubleToFloat, Origin(), finalAdd);
+ tail->appendNew<ControlValue>(proc, Return, Origin(), floatResult);
+
+ auto code = compile(proc);
+ CHECK(isIdentical(invoke<float>(*code, 1, bitwise_cast<int32_t>(value)), value + value));
+ CHECK(isIdentical(invoke<float>(*code, 0, bitwise_cast<int32_t>(value)), static_cast<float>(constValue + value)));
+}
+
void testDoubleArgToInt64BitwiseCast(double value)
{
Procedure proc;
@@ -5794,6 +6053,25 @@
}
}
+void testStoreDoubleConstantAsFloat(double input)
+{
+ // Simple store from an address in a register.
+ Procedure proc;
+ BasicBlock* root = proc.addBlock();
+ Value* value = root->appendNew<ConstDoubleValue>(proc, Origin(), input);
+ Value* valueAsFloat = root->appendNew<Value>(proc, DoubleToFloat, Origin(), value);
+
+ Value* destinationAddress = root->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0);
+
+ root->appendNew<MemoryValue>(proc, Store, Origin(), valueAsFloat, destinationAddress);
+
+ root->appendNew<ControlValue>(proc, Return, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0));
+
+ float output = 0.;
+ CHECK(!compileAndRun<int64_t>(proc, input, &output));
+ CHECK(isIdentical(static_cast<float>(input), output));
+}
+
void testSpillGP()
{
Procedure proc;
@@ -12147,6 +12425,14 @@
RUN_UNARY(testSqrtArgWithUselessDoubleConversion, floatingPointOperands<float>());
RUN_UNARY(testSqrtArgWithEffectfulDoubleConversion, floatingPointOperands<float>());
+ RUN_BINARY(testCompareTwoFloatToDouble, floatingPointOperands<float>(), floatingPointOperands<float>());
+ RUN_BINARY(testCompareOneFloatToDouble, floatingPointOperands<float>(), floatingPointOperands<double>());
+ RUN_BINARY(testCompareFloatToDoubleThroughPhi, floatingPointOperands<float>(), floatingPointOperands<float>());
+ RUN_UNARY(testDoubleToFloatThroughPhi, floatingPointOperands<float>());
+ RUN_UNARY(testDoubleProducerPhiToFloatConversion, floatingPointOperands<float>());
+ RUN_UNARY(testDoubleProducerPhiToFloatConversionWithDoubleConsumer, floatingPointOperands<float>());
+ RUN_BINARY(testDoubleProducerPhiWithNonFloatConst, floatingPointOperands<float>(), floatingPointOperands<double>());
+
RUN_UNARY(testDoubleArgToInt64BitwiseCast, floatingPointOperands<double>());
RUN_UNARY(testDoubleImmToInt64BitwiseCast, floatingPointOperands<double>());
RUN_UNARY(testTwoBitwiseCastOnDouble, floatingPointOperands<double>());
@@ -12172,6 +12458,7 @@
RUN_UNARY(testConvertFloatToDoubleMem, floatingPointOperands<float>());
RUN_UNARY(testConvertDoubleToFloatToDoubleToFloat, floatingPointOperands<double>());
RUN_UNARY(testStoreFloat, floatingPointOperands<double>());
+ RUN_UNARY(testStoreDoubleConstantAsFloat, floatingPointOperands<double>());
RUN_UNARY(testLoadFloatConvertDoubleConvertFloatStoreFloat, floatingPointOperands<float>());
RUN_UNARY(testFroundArg, floatingPointOperands<double>());
RUN_UNARY(testFroundMem, floatingPointOperands<double>());