Propagate phi specializations (bug 685993, r=sstangl).
authorDavid Anderson <danderson@mozilla.com>
Mon, 26 Sep 2011 13:35:14 -0700
changeset 105263 1cc1a573bb994b531665dc1468011ec4221f531f
parent 105262 ef5f3216118471534d4950113a5fa49a1be257e0
child 105264 acf3c1fb7c945174bcf15a7aeab60ef1b03b0791
push id14706
push usereakhgari@mozilla.com
push dateTue, 11 Sep 2012 20:39:52 +0000
treeherdermozilla-inbound@d50bf1edaabe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssstangl
bugs685993
milestone9.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Propagate phi specializations (bug 685993, r=sstangl).
js/src/ion/IonAnalysis.cpp
js/src/ion/IonLIR.cpp
js/src/ion/MIR.cpp
js/src/ion/MIR.h
js/src/ion/Snapshots.cpp
js/src/ion/TypeOracle.h
--- a/js/src/ion/IonAnalysis.cpp
+++ b/js/src/ion/IonAnalysis.cpp
@@ -380,17 +380,25 @@ TypeAnalyzer::specializePhi(MPhi *phi)
     // If it's a value, just give up and leave the phi unspecialized.
     if (first == MIRType_Value) {
         despecializePhi(phi);
         return;
     }
 
     for (size_t i = 1; i < phi->numOperands(); i++) {
         MDefinition *other = phi->getOperand(i);
-        if (GetObservedType(other) != first) {
+        MIRType otherType = GetObservedType(other);
+        if (otherType != first) {
+            if (IsNumberType(otherType) && IsNumberType(first)) {
+                // Allow coercion between int/double, and force the phi to be
+                // double.
+                first = MIRType_Double;
+                continue;
+            }
+            // Any other type mismatches are fatal.
             despecializePhi(phi);
             return;
         }
     }
 
     if (phi->type() == first)
         return;
 
@@ -494,23 +502,37 @@ TypeAnalyzer::adjustOutput(MDefinition *
             use++;
     }
 }
 
 void
 TypeAnalyzer::adjustPhiInputs(MPhi *phi)
 {
     // If the phi returns a specific type, assert that its inputs are correct.
-    if (phi->type() != MIRType_Value) {
-#ifdef DEBUG
+    MIRType phiType = phi->type();
+    if (phiType != MIRType_Value) {
         for (size_t i = 0; i < phi->numOperands(); i++) {
             MDefinition *in = phi->getOperand(i);
+            MIRType inType = GetObservedType(in);
+
+            if (phiType == MIRType_Double && inType == MIRType_Int32) {
+                MToDouble *convert = MToDouble::New(in);
+
+                // Note that we're relying on the fact that |in| is guaranteed
+                // to become |int| even if its current return type is not
+                // |int|. We're absolutely not allowed to change the observable
+                // type of the input here, only its representation.
+                MBasicBlock *pred = phi->block()->getPredecessor(i);
+                pred->insertBefore(pred->lastIns(), convert);
+                phi->replaceOperand(i, convert);
+                continue;
+            }
+
             JS_ASSERT(GetObservedType(in) == phi->type());
         }
-#endif
         return;
     }
 
     // Box every typed input.
     for (size_t i = 0; i < phi->numOperands(); i++) {
         MDefinition *in = phi->getOperand(i);
         if (in->type() == MIRType_Value)
             continue;
--- a/js/src/ion/IonLIR.cpp
+++ b/js/src/ion/IonLIR.cpp
@@ -248,18 +248,26 @@ LInstruction::printOperands(FILE *fp)
     }
 }
 
 void
 LInstruction::assignSnapshot(LSnapshot *snapshot)
 {
     JS_ASSERT(!snapshot_);
     snapshot_ = snapshot;
-    IonSpew(IonSpew_Snapshots, "Assigning snapshot %p to instruction %p",
-            (void *)snapshot, (void *)this);
+
+#ifdef DEBUG
+    if (IonSpewEnabled(IonSpew_Snapshots)) {
+        IonSpewHeader(IonSpew_Snapshots);
+        fprintf(IonSpewFile, "Assigning snapshot %p to instruction %p (",
+                (void *)snapshot, (void *)this);
+        printName(IonSpewFile);
+        fprintf(IonSpewFile, ")\n");
+    }
+#endif
 }
 
 void
 LInstruction::print(FILE *fp)
 {
     printName(fp);
 
     fprintf(fp, " (");
--- a/js/src/ion/MIR.cpp
+++ b/js/src/ion/MIR.cpp
@@ -37,16 +37,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "IonBuilder.h"
 #include "MIR.h"
 #include "MIRGraph.h"
+#include "jsnum.h"
 
 using namespace js;
 using namespace js::ion;
 
 static void
 PrintOpcodeName(FILE *fp, MDefinition::Opcode op)
 {
     static const char *names[] =
@@ -703,8 +704,25 @@ MResumePoint::init(MBasicBlock *block)
 
 void
 MResumePoint::inherit(MBasicBlock *block)
 {
     for (size_t i = 0; i < stackDepth(); i++)
         initOperand(i, block->getSlot(i));
 }
 
+MDefinition *
+MToDouble::foldsTo(bool useValueNumbers)
+{
+    if (input()->isConstant()) {
+        const Value &v = input()->toConstant()->value();
+        if (v.isPrimitive()) {
+            double out;
+            DebugOnly<bool> ok = ToNumber(GetIonContext()->cx, v, &out);
+            JS_ASSERT(ok);
+
+            return MConstant::New(DoubleValue(out));
+        }
+    }
+
+    return this;
+}
+
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -1094,16 +1094,17 @@ class MToDouble : public MUnaryInstructi
 
   public:
     INSTRUCTION_HEADER(ToDouble);
     static MToDouble *New(MDefinition *def)
     {
         return new MToDouble(def);
     }
 
+    MDefinition *foldsTo(bool useValueNumbers);
     MDefinition *input() const {
         return getOperand(0);
     }
 };
 
 // Converts a primitive (either typed or untyped) to an int32. If the input is
 // not primitive at runtime, a bailout occurs. If the input cannot be converted
 // to an int32 without loss (i.e. "5.5" or undefined) then a bailout occurs.
--- a/js/src/ion/Snapshots.cpp
+++ b/js/src/ion/Snapshots.cpp
@@ -413,18 +413,18 @@ SnapshotWriter::endSnapshot()
     // Check that the last write succeeded.
     JS_ASSERT(nslots_ == slotsWritten_);
 
     // Place a sentinel for asserting on the other end.
 #ifdef DEBUG
     writer_.writeSigned(-1);
 #endif
     
-    IonSpew(IonSpew_Snapshots, "    total size: %d bytes",
-            uint32(writer_.length() - lastStart_));
+    IonSpew(IonSpew_Snapshots, "    total size: %d bytes (start %d)",
+            uint32(writer_.length() - lastStart_), lastStart_);
 }
 
 void
 SnapshotWriter::addInt32Slot(int32 value)
 {
     IonSpew(IonSpew_Snapshots, "    slot %d: int32 %d", slotsWritten_, value);
 
     if (value >= 0 && uint32(value) < SINGLETON_VALUE) {
--- a/js/src/ion/TypeOracle.h
+++ b/js/src/ion/TypeOracle.h
@@ -167,13 +167,19 @@ StringFromMIRType(MIRType type)
     case MIRType_None:
       return "None";
     default:
       JS_NOT_REACHED("Unknown MIRType.");
       return "";
   }
 }
 
+static inline bool
+IsNumberType(MIRType type)
+{
+    return type == MIRType_Int32 || type == MIRType_Double;
+}
+
 } /* ion */
 } /* js */
 
 #endif // js_ion_type_oracle_h__