Title: [199648] trunk/Source/_javascript_Core
Revision
199648
Author
benja...@webkit.org
Date
2016-04-17 22:13:13 -0700 (Sun, 17 Apr 2016)

Log Message

[JSC] ReduceDoubleToFloat should work accross Phis
https://bugs.webkit.org/show_bug.cgi?id=156603
<rdar://problem/25736205>

Patch by Benjamin Poulain <bpoul...@apple.com> on 2016-04-17
Reviewed by Saam Barati and Filip Pizlo.

This patch extends B3's ReduceDoubleToFloat phase to work accross
Upsilon-Phis. This is important to optimize loops and some crazy cases.

In its simplest form, we can have conversion propagated from something
like this:
    Double @1 = Phi()
    Float @2 = DoubleToFloat(@1)

When that happens, we just need to propagate that the result only
need float precision accross all values coming to this Phi.


There are more complicated cases when the value produced is effectively Float
but the user of the value does not do DoubleToFloat.

Typically, we have something like:
    #1
        @1 = ConstDouble(1)
        @2 = Upsilon(@1, ^5)
    #2
        @3 = FloatToDouble(@x)
        @4 = Upsilon(@3, ^5)
    #3
        @5 = Phi()
        @6 = Add(@5, @somethingFloat)
        @7 = DoubleToFloat(@6)

Here with a Phi-Upsilon that is a Double but can be represented
as Float without loss of precision.

It is valuable to convert such Phis to float if and only if the value
is used as float. Otherwise, you may be just adding useless conversions
(for example, two double constants that flow into a double Add should not
turn into two float constant flowing into a FloatToDouble then Add).


ReduceDoubleToFloat do two analysis passes to gather the necessary
meta information. Then we have a simplify() phase to actually reduce
operation. Finally, the cleanup() pass put the graph into a valid
state again.

The two analysis passes work by disproving that something is float.
-findCandidates() accumulates anything used as Double.
-findPhisContainingFloat() accumulates phis that would lose precision
 by converting the input to float.

With this change, Unity3D improves by ~1.5%, box2d-f32 improves
by ~2.8% (on Haswell).

* b3/B3ReduceDoubleToFloat.cpp:
(JSC::B3::reduceDoubleToFloat):
* b3/testb3.cpp:
(JSC::B3::testCompareTwoFloatToDouble):
(JSC::B3::testCompareOneFloatToDouble):
(JSC::B3::testCompareFloatToDoubleThroughPhi):
(JSC::B3::testDoubleToFloatThroughPhi):
(JSC::B3::testDoubleProducerPhiToFloatConversion):
(JSC::B3::testDoubleProducerPhiToFloatConversionWithDoubleConsumer):
(JSC::B3::testDoubleProducerPhiWithNonFloatConst):
(JSC::B3::testStoreDoubleConstantAsFloat):
(JSC::B3::run):
* tests/stress/double-compare-to-float.js: Added.
(canSimplifyToFloat):
(canSimplifyToFloatWithConstant):
(cannotSimplifyA):
(cannotSimplifyB):
* tests/stress/double-to-float.js: Added.
(upsilonReferencingItsPhi):
(upsilonReferencingItsPhiAllFloat):
(upsilonReferencingItsPhiWithoutConversion):
(conversionPropagages):
(chainedUpsilonBothConvert):
(chainedUpsilonFirstConvert):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (199647 => 199648)


--- trunk/Source/_javascript_Core/ChangeLog	2016-04-17 21:53:11 UTC (rev 199647)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-04-18 05:13:13 UTC (rev 199648)
@@ -1,3 +1,85 @@
+2016-04-17  Benjamin Poulain  <bpoul...@apple.com>
+
+        [JSC] ReduceDoubleToFloat should work accross Phis
+        https://bugs.webkit.org/show_bug.cgi?id=156603
+        <rdar://problem/25736205>
+
+        Reviewed by Saam Barati and Filip Pizlo.
+
+        This patch extends B3's ReduceDoubleToFloat phase to work accross
+        Upsilon-Phis. This is important to optimize loops and some crazy cases.
+
+        In its simplest form, we can have conversion propagated from something
+        like this:
+            Double @1 = Phi()
+            Float @2 = DoubleToFloat(@1)
+
+        When that happens, we just need to propagate that the result only
+        need float precision accross all values coming to this Phi.
+
+
+        There are more complicated cases when the value produced is effectively Float
+        but the user of the value does not do DoubleToFloat.
+
+        Typically, we have something like:
+            #1
+                @1 = ConstDouble(1)
+                @2 = Upsilon(@1, ^5)
+            #2
+                @3 = FloatToDouble(@x)
+                @4 = Upsilon(@3, ^5)
+            #3
+                @5 = Phi()
+                @6 = Add(@5, @somethingFloat)
+                @7 = DoubleToFloat(@6)
+
+        Here with a Phi-Upsilon that is a Double but can be represented
+        as Float without loss of precision.
+
+        It is valuable to convert such Phis to float if and only if the value
+        is used as float. Otherwise, you may be just adding useless conversions
+        (for example, two double constants that flow into a double Add should not
+        turn into two float constant flowing into a FloatToDouble then Add).
+
+
+        ReduceDoubleToFloat do two analysis passes to gather the necessary
+        meta information. Then we have a simplify() phase to actually reduce
+        operation. Finally, the cleanup() pass put the graph into a valid
+        state again.
+
+        The two analysis passes work by disproving that something is float.
+        -findCandidates() accumulates anything used as Double.
+        -findPhisContainingFloat() accumulates phis that would lose precision
+         by converting the input to float.
+
+        With this change, Unity3D improves by ~1.5%, box2d-f32 improves
+        by ~2.8% (on Haswell).
+
+        * b3/B3ReduceDoubleToFloat.cpp:
+        (JSC::B3::reduceDoubleToFloat):
+        * b3/testb3.cpp:
+        (JSC::B3::testCompareTwoFloatToDouble):
+        (JSC::B3::testCompareOneFloatToDouble):
+        (JSC::B3::testCompareFloatToDoubleThroughPhi):
+        (JSC::B3::testDoubleToFloatThroughPhi):
+        (JSC::B3::testDoubleProducerPhiToFloatConversion):
+        (JSC::B3::testDoubleProducerPhiToFloatConversionWithDoubleConsumer):
+        (JSC::B3::testDoubleProducerPhiWithNonFloatConst):
+        (JSC::B3::testStoreDoubleConstantAsFloat):
+        (JSC::B3::run):
+        * tests/stress/double-compare-to-float.js: Added.
+        (canSimplifyToFloat):
+        (canSimplifyToFloatWithConstant):
+        (cannotSimplifyA):
+        (cannotSimplifyB):
+        * tests/stress/double-to-float.js: Added.
+        (upsilonReferencingItsPhi):
+        (upsilonReferencingItsPhiAllFloat):
+        (upsilonReferencingItsPhiWithoutConversion):
+        (conversionPropagages):
+        (chainedUpsilonBothConvert):
+        (chainedUpsilonFirstConvert):
+
 2016-04-17  Yusuke Suzuki  <utatane....@gmail.com>
 
         [ES6] Use @isObject to check Object Type instead of using instanceof

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>());

Added: trunk/Source/_javascript_Core/tests/stress/double-compare-to-float.js (0 => 199648)


--- trunk/Source/_javascript_Core/tests/stress/double-compare-to-float.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/double-compare-to-float.js	2016-04-18 05:13:13 UTC (rev 199648)
@@ -0,0 +1,49 @@
+function canSimplifyToFloat(a, b)
+{
+    return Math.fround(a) === Math.fround(b);
+}
+noInline(canSimplifyToFloat);
+
+function canSimplifyToFloatWithConstant(a)
+{
+    return Math.fround(a) === 1.0;
+}
+noInline(canSimplifyToFloatWithConstant);
+
+function cannotSimplifyA(a, b)
+{
+    return a === Math.fround(b);
+}
+noInline(cannotSimplifyA);
+
+function cannotSimplifyB(a, b)
+{
+    return Math.fround(a) === b;
+}
+noInline(cannotSimplifyB);
+
+for (let i = 1; i < 1e4; ++i) {
+    if (canSimplifyToFloat(Math.PI, Math.PI) !== true)
+        throw "Failed canSimplifyToFloat(Math.PI, Math.PI)";
+    if (canSimplifyToFloat(Math.LN2, Math.PI) !== false)
+        throw "Failed canSimplifyToFloat(Math.LN2, Math.PI)";
+
+    if (canSimplifyToFloatWithConstant(Math.PI) !== false)
+        throw "Failed canSimplifyToFloatWithConstant(Math.PI)";
+    if (canSimplifyToFloatWithConstant(1) !== true)
+        throw "Failed canSimplifyToFloatWithConstant(1)";
+
+    if (cannotSimplifyA(Math.PI, Math.PI) !== false)
+        throw "Failed cannotSimplifyA(Math.PI, Math.PI)";
+    if (cannotSimplifyA(Math.fround(Math.PI), Math.PI) !== true)
+        throw "Failed cannotSimplifyA(Math.round(Math.PI), Math.PI)";
+    if (cannotSimplifyA(Math.LN2, Math.PI) !== false)
+        throw "Failed cannotSimplifyA(Math.LN2, Math.PI)";
+
+    if (cannotSimplifyB(Math.PI, Math.PI) !== false)
+        throw "Failed cannotSimplifyA(Math.PI, Math.PI)";
+    if (cannotSimplifyB(Math.PI, Math.fround(Math.PI)) !== true)
+        throw "Failed cannotSimplifyA(Math.round(Math.PI), Math.PI)";
+    if (cannotSimplifyB(Math.LN2, Math.PI) !== false)
+        throw "Failed cannotSimplifyA(Math.LN2, Math.PI)";
+}

Added: trunk/Source/_javascript_Core/tests/stress/double-to-float.js (0 => 199648)


--- trunk/Source/_javascript_Core/tests/stress/double-to-float.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/double-to-float.js	2016-04-18 05:13:13 UTC (rev 199648)
@@ -0,0 +1,157 @@
+function upsilonReferencingItsPhi(index, input)
+{
+    // All uses of "outputDouble" are converted to float.
+    // Inside the loop, the Upsilon is referencing its own Phi. This should
+    // not prevent the conversion.
+    let outputDouble = input;
+    while (index) {
+        if (index & 0x4)
+            outputDouble = Math.fround(outputDouble) + Math.PI;
+        index = index >>> 1;
+    }
+    return Math.fround(outputDouble);
+}
+noInline(upsilonReferencingItsPhi);
+
+let expectedNotTaken = Math.fround(Math.LN2);
+let expectedTaken = Math.fround(Math.fround(Math.LN2) + Math.PI);
+for (let i = 0; i < 1e6; ++i) {
+    let branchNotTakenResult = upsilonReferencingItsPhi(3, Math.LN2);
+    if (branchNotTakenResult !== expectedNotTaken)
+        throw "Failed upsilonReferencingItsPhi(3, Math.LN2) at i = " + i + " result = " + branchNotTakenResult;
+
+    let branchTakenResult = upsilonReferencingItsPhi(7, Math.LN2);
+    if (branchTakenResult !== expectedTaken)
+        throw "Failed upsilonReferencingItsPhi(7, Math.LN2) at i = " + i + " result = " + branchTakenResult;
+}
+
+// Same as above, but this time it is always better to convert the outside Phi-Upsilon.
+function upsilonReferencingItsPhiAllFloat(index, input)
+{
+    let outputDouble = Math.fround(input);
+    while (index) {
+        if (index & 0x4)
+            outputDouble = Math.fround(outputDouble) + Math.PI;
+        index = index >>> 1;
+    }
+    return Math.fround(outputDouble);
+}
+noInline(upsilonReferencingItsPhiAllFloat);
+
+for (let i = 0; i < 1e6; ++i) {
+    let branchNotTakenResult = upsilonReferencingItsPhiAllFloat(3, Math.LN2);
+    if (branchNotTakenResult !== expectedNotTaken)
+        throw "Failed upsilonReferencingItsPhiAllFloat(3, Math.LN2) at i = " + i + " result = " + branchNotTakenResult;
+
+    let branchTakenResult = upsilonReferencingItsPhiAllFloat(7, Math.LN2);
+    if (branchTakenResult !== expectedTaken)
+        throw "Failed upsilonReferencingItsPhiAllFloat(7, Math.LN2) at i = " + i + " result = " + branchTakenResult;
+}
+
+// This time, converting to float would be a mistake because one of the Phi
+// is not converted.
+function upsilonReferencingItsPhiWithoutConversion(index, input)
+{
+    let outputDouble = input;
+    while (index) {
+        if (index & 0x4)
+            outputDouble = Math.fround(outputDouble) + Math.PI;
+        index = index >>> 1;
+    }
+    return outputDouble;
+}
+noInline(upsilonReferencingItsPhiWithoutConversion);
+
+let expectedNotTakenWithoutConversion = Math.LN2;
+let expectedTakenWithoutConversion = Math.fround(Math.LN2) + Math.PI;
+for (let i = 0; i < 1e6; ++i) {
+    let branchNotTakenResult = upsilonReferencingItsPhiWithoutConversion(3, Math.LN2);
+    if (branchNotTakenResult !== expectedNotTakenWithoutConversion)
+        throw "Failed upsilonReferencingItsPhiWithoutConversion(3, Math.LN2) at i = " + i + " result = " + branchNotTakenResult;
+
+    let branchTakenResult = upsilonReferencingItsPhiWithoutConversion(7, Math.LN2);
+    if (branchTakenResult !== expectedTakenWithoutConversion)
+        throw "Failed upsilonReferencingItsPhiWithoutConversion(7, Math.LN2) at i = " + i + " result = " + branchTakenResult;
+}
+
+function conversionPropagages(flags, a, b)
+{
+    let result = 0.5;
+    if (flags & 0x1) {
+        if (flags & 0x2) {
+            if (flags & 0x4) {
+                if (flags & 0x8) {
+                    result = Math.fround(a) + Math.fround(b);
+                } else {
+                    result = 6.5;
+                }
+            } else {
+                result = 4.5;
+            }
+        } else {
+            result = 2.5;
+        }
+    } else {
+        result = 1.5;
+    }
+    return Math.fround(result);
+}
+noInline(conversionPropagages);
+
+let conversionPropagageExpectedResult = Math.fround(Math.fround(Math.LN2) + Math.fround(Math.PI));
+for (let i = 0; i < 1e6; ++i) {
+    let result = conversionPropagages(0xf, Math.LN2, Math.PI);
+    if (result !== conversionPropagageExpectedResult)
+        throw "Failed conversionPropagages(0xf, Math.LN2, Math.PI)";
+}
+
+
+function chainedUpsilonBothConvert(condition1, condition2, a, b)
+{
+    let firstPhi;
+    if (condition1)
+        firstPhi = Math.fround(a);
+    else
+        firstPhi = Math.fround(b);
+
+    let secondPhi;
+    if (condition2)
+        secondPhi = firstPhi + 2;
+    else
+        secondPhi = firstPhi + 1;
+    return Math.fround(secondPhi);
+}
+noInline(chainedUpsilonBothConvert);
+
+let expectedChainedUpsilonBothConvert = Math.fround(Math.fround(Math.PI) + Math.fround(1));
+for (let i = 0; i < 1e6; ++i) {
+    if (chainedUpsilonBothConvert(1, 0, Math.PI, Math.LN2) !== expectedChainedUpsilonBothConvert)
+        throw "Failed chainedUpsilonBothConvert(1, 0, Math.PI, Math.LN2)";
+}
+
+function chainedUpsilonFirstConvert(condition1, condition2, a, b)
+{
+    // This first phi is trivially simplified by the fround()
+    // of the second if-else.
+    let firstPhi;
+    if (condition1)
+        firstPhi = Math.fround(a);
+    else
+        firstPhi = Math.fround(b);
+
+    // This second one cannot ever be converted because the
+    // result is not rounded to float.
+    let secondPhi;
+    if (condition2)
+        secondPhi = Math.fround(firstPhi) + Math.fround(1/3);
+    else
+        secondPhi = Math.fround(firstPhi) - Math.fround(1/3);
+    return secondPhi;
+}
+noInline(chainedUpsilonFirstConvert);
+
+let expectedChainedUpsilonFirstConvert = Math.fround(Math.PI) - Math.fround(1/3);
+for (let i = 0; i < 1e6; ++i) {
+    if (chainedUpsilonFirstConvert(1, 0, Math.PI, Math.LN2) !== expectedChainedUpsilonFirstConvert)
+        throw "Failed chainedUpsilonFirstConvert(1, 0, Math.PI, Math.LN2)";
+}
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to