Bug 1046870 - Add Spew to follow escape analysis reasons. r=jandem
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Thu, 21 Aug 2014 21:48:23 +0200
changeset 200874 67d6e6c48b7a03bdfb22b9bbf3326330f1b53bfe
parent 200873 48f6eb406821fa76e527fc7cec8b371b0422b557
child 200875 d0dd7f70b560e7efed5605bef252389b2e45c706
push id48025
push usernpierron@mozilla.com
push dateThu, 21 Aug 2014 19:48:51 +0000
treeherdermozilla-inbound@d0dd7f70b560 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1046870
milestone34.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
Bug 1046870 - Add Spew to follow escape analysis reasons. r=jandem
js/src/jit/IonSpewer.cpp
js/src/jit/IonSpewer.h
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/ScalarReplacement.cpp
--- a/js/src/jit/IonSpewer.cpp
+++ b/js/src/jit/IonSpewer.cpp
@@ -4,16 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef DEBUG
 
 #include "jit/IonSpewer.h"
 
 #include "jit/Ion.h"
+#include "jit/MIR.h"
+
 #include "vm/HelperThreads.h"
 
 #ifndef ION_SPEW_DIR
 # if defined(_WIN32)
 #  define ION_SPEW_DIR ""
 # elif defined(__ANDROID__)
 #  define ION_SPEW_DIR "/data/local/tmp/"
 # else
@@ -229,16 +231,17 @@ jit::CheckLogging()
         fflush(nullptr);
         printf(
             "\n"
             "usage: IONFLAGS=option,option,option,... where options can be:\n"
             "\n"
             "  aborts     Compilation abort messages\n"
             "  scripts    Compiled scripts\n"
             "  mir        MIR information\n"
+            "  escape     Escape analysis\n"
             "  alias      Alias analysis\n"
             "  gvn        Global Value Numbering\n"
             "  licm       Loop invariant code motion\n"
             "  regalloc   Register allocation\n"
             "  inline     Inlining\n"
             "  snapshots  Snapshot information\n"
             "  codegen    Native code generation\n"
             "  bailouts   Bailouts\n"
@@ -264,16 +267,18 @@ jit::CheckLogging()
             "  bl-all     All baseline spew\n"
             "\n"
         );
         exit(0);
         /*NOTREACHED*/
     }
     if (ContainsFlag(env, "aborts"))
         EnableChannel(IonSpew_Abort);
+    if (ContainsFlag(env, "escape"))
+        EnableChannel(IonSpew_Escape);
     if (ContainsFlag(env, "alias"))
         EnableChannel(IonSpew_Alias);
     if (ContainsFlag(env, "scripts"))
         EnableChannel(IonSpew_Scripts);
     if (ContainsFlag(env, "mir"))
         EnableChannel(IonSpew_MIR);
     if (ContainsFlag(env, "gvn"))
         EnableChannel(IonSpew_GVN);
@@ -380,16 +385,28 @@ jit::IonSpew(IonSpewChannel channel, con
 {
     va_list ap;
     va_start(ap, fmt);
     IonSpewVA(channel, fmt, ap);
     va_end(ap);
 }
 
 void
+jit::IonSpewDef(IonSpewChannel channel, const char *str, MDefinition *def)
+{
+    if (!IonSpewEnabled(channel))
+        return;
+
+    IonSpewHeader(channel);
+    fprintf(IonSpewFile, str);
+    def->dump(IonSpewFile);
+    def->dumpLocation(IonSpewFile);
+}
+
+void
 jit::IonSpewStart(IonSpewChannel channel, const char *fmt, ...)
 {
     va_list ap;
     va_start(ap, fmt);
     IonSpewStartVA(channel, fmt, ap);
     va_end(ap);
 }
 void
--- a/js/src/jit/IonSpewer.h
+++ b/js/src/jit/IonSpewer.h
@@ -23,16 +23,18 @@ namespace jit {
     /* Used to abort SSA construction */    \
     _(Abort)                                \
     /* Information about compiled scripts */\
     _(Scripts)                              \
     /* Info about failing to log script */  \
     _(Logs)                                 \
     /* Information during MIR building */   \
     _(MIR)                                  \
+    /* Information during escape analysis */\
+    _(Escape)                               \
     /* Information during alias analysis */ \
     _(Alias)                                \
     /* Information during GVN */            \
     _(GVN)                                  \
     /* Information during Range analysis */ \
     _(Range)                                \
     /* Information during loop unrolling */ \
     _(Unrolling)                            \
@@ -137,16 +139,17 @@ void IonSpew(IonSpewChannel channel, con
 void IonSpewStart(IonSpewChannel channel, const char *fmt, ...);
 void IonSpewCont(IonSpewChannel channel, const char *fmt, ...);
 void IonSpewFin(IonSpewChannel channel);
 void IonSpewHeader(IonSpewChannel channel);
 bool IonSpewEnabled(IonSpewChannel channel);
 void IonSpewVA(IonSpewChannel channel, const char *fmt, va_list ap);
 void IonSpewStartVA(IonSpewChannel channel, const char *fmt, va_list ap);
 void IonSpewContVA(IonSpewChannel channel, const char *fmt, va_list ap);
+void IonSpewDef(IonSpewChannel channel, const char *str, MDefinition *def);
 
 void EnableChannel(IonSpewChannel channel);
 void DisableChannel(IonSpewChannel channel);
 void EnableIonDebugLogging();
 
 #else
 
 static inline void IonSpewNewFunction(MIRGraph *graph, JS::HandleScript function)
@@ -171,16 +174,18 @@ static inline void IonSpewFin(IonSpewCha
 { }
 
 static inline void IonSpewHeader(IonSpewChannel channel)
 { }
 static inline bool IonSpewEnabled(IonSpewChannel channel)
 { return false; }
 static inline void IonSpewVA(IonSpewChannel channel, const char *fmt, va_list ap)
 { }
+static inline void IonSpewDef(IonSpewChannel channel, const char *str, MDefinition *def)
+{ }
 
 static inline void EnableChannel(IonSpewChannel)
 { }
 static inline void DisableChannel(IonSpewChannel)
 { }
 static inline void EnableIonDebugLogging()
 { }
 
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -352,16 +352,44 @@ MDefinition::dump(FILE *fp) const
 }
 
 void
 MDefinition::dump() const
 {
     dump(stderr);
 }
 
+void
+MDefinition::dumpLocation(FILE *fp) const
+{
+    MResumePoint *rp = nullptr;
+    const char *linkWord = nullptr;
+    if (isInstruction() && toInstruction()->resumePoint()) {
+        rp = toInstruction()->resumePoint();
+        linkWord = "at";
+    } else {
+        rp = block()->entryResumePoint();
+        linkWord = "after";
+    }
+
+    while (rp) {
+        JSScript *script = rp->block()->info().script();
+        uint32_t lineno = PCToLineNumber(rp->block()->info().script(), rp->pc());
+        fprintf(fp, "  %s %s:%d\n", linkWord, script->filename(), lineno);
+        rp = rp->caller();
+        linkWord = "in";
+    }
+}
+
+void
+MDefinition::dumpLocation() const
+{
+    dumpLocation(stderr);
+}
+
 #ifdef DEBUG
 size_t
 MDefinition::useCount() const
 {
     size_t count = 0;
     for (MUseIterator i(uses_.begin()); i != uses_.end(); i++)
         count++;
     return count;
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -408,16 +408,18 @@ class MDefinition : public MNode
     virtual const char *opName() const = 0;
     virtual bool accept(MDefinitionVisitor *visitor) = 0;
 
     void printName(FILE *fp) const;
     static void PrintOpcodeName(FILE *fp, Opcode op);
     virtual void printOpcode(FILE *fp) const;
     void dump(FILE *fp) const;
     void dump() const;
+    void dumpLocation(FILE *fp) const;
+    void dumpLocation() const;
 
     // For LICM.
     virtual bool neverHoist() const { return false; }
 
     // Also for LICM. Test whether this definition is likely to be a call, which
     // would clobber all or many of the floating-point registers, such that
     // hoisting floating-point constants out of containing loops isn't likely to
     // be worthwhile.
--- a/js/src/jit/ScalarReplacement.cpp
+++ b/js/src/jit/ScalarReplacement.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/ScalarReplacement.h"
 
 #include "mozilla/Vector.h"
 
 #include "jit/IonAnalysis.h"
+#include "jit/IonSpewer.h"
 #include "jit/MIR.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MIRGraph.h"
 
 namespace js {
 namespace jit {
 
 // Scan resume point operands in search of a local variable which captures the
@@ -140,28 +141,33 @@ IsObjectEscaped(MInstruction *ins)
 
     // Check if the object is escaped. If the object is not the first argument
     // of either a known Store / Load, then we consider it as escaped. This is a
     // cheap and conservative escape analysis.
     for (MUseIterator i(ins->usesBegin()); i != ins->usesEnd(); i++) {
         MNode *consumer = (*i)->consumer();
         if (!consumer->isDefinition()) {
             // Cannot optimize if it is observable from fun.arguments or others.
-            if (consumer->toResumePoint()->isObservableOperand(*i))
+            if (consumer->toResumePoint()->isObservableOperand(*i)) {
+                IonSpewDef(IonSpew_Escape, "Object is observable\n", ins);
                 return true;
+            }
             continue;
         }
 
         MDefinition *def = consumer->toDefinition();
         switch (def->op()) {
           case MDefinition::Op_StoreFixedSlot:
           case MDefinition::Op_LoadFixedSlot:
             // Not escaped if it is the first argument.
             if (def->indexOf(*i) == 0)
                 break;
+
+            IonSpewDef(IonSpew_Escape, "Object ", ins);
+            IonSpewDef(IonSpew_Escape, "  is escaped by\n", def);
             return true;
 
           case MDefinition::Op_Slots: {
 #ifdef DEBUG
             // Assert that MSlots are only used by MStoreSlot and MLoadSlot.
             MSlots *ins = def->toSlots();
             MOZ_ASSERT(ins->object() != 0);
             for (MUseIterator i(ins->usesBegin()); i != ins->usesEnd(); i++) {
@@ -173,27 +179,33 @@ IsObjectEscaped(MInstruction *ins)
             }
 #endif
             break;
           }
 
           case MDefinition::Op_GuardShape: {
             MGuardShape *guard = def->toGuardShape();
             MOZ_ASSERT(!ins->isGuardShape());
-            if (ins->toNewObject()->templateObject()->lastProperty() != guard->shape())
+            if (ins->toNewObject()->templateObject()->lastProperty() != guard->shape()) {
+                IonSpewDef(IonSpew_Escape, "Object ", ins);
+                IonSpewDef(IonSpew_Escape, "  has a non-matching guard shape\n", guard);
                 return true;
+            }
             if (IsObjectEscaped(def->toInstruction()))
                 return true;
             break;
           }
           default:
+            IonSpewDef(IonSpew_Escape, "Object ", ins);
+            IonSpewDef(IonSpew_Escape, "  is escaped by\n", def);
             return true;
         }
     }
 
+    IonSpewDef(IonSpew_Escape, "Object is not escaped\n", ins);
     return false;
 }
 
 class ObjectMemoryView : public MDefinitionVisitorDefaultNoop
 {
   public:
     typedef MObjectState BlockState;
     static const char *phaseName;
@@ -484,28 +496,37 @@ static bool
 IsArrayEscaped(MInstruction *ins)
 {
     MOZ_ASSERT(ins->type() == MIRType_Object);
     MOZ_ASSERT(ins->isNewArray());
     uint32_t count = ins->toNewArray()->count();
 
     // The array is probably too large to be represented efficiently with
     // MArrayState, and we do not want to make huge allocations during bailouts.
-    if (!ins->toNewArray()->isAllocating() || count >= 16)
+    if (!ins->toNewArray()->isAllocating()) {
+        IonSpewDef(IonSpew_Escape, "Array is not allocated\n", ins);
         return true;
+    }
+
+    if (count >= 16) {
+        IonSpewDef(IonSpew_Escape, "Array has too many elements\n", ins);
+        return true;
+    }
 
     // Check if the object is escaped. If the object is not the first argument
     // of either a known Store / Load, then we consider it as escaped. This is a
     // cheap and conservative escape analysis.
     for (MUseIterator i(ins->usesBegin()); i != ins->usesEnd(); i++) {
         MNode *consumer = (*i)->consumer();
         if (!consumer->isDefinition()) {
             // Cannot optimize if it is observable from fun.arguments or others.
-            if (consumer->toResumePoint()->isObservableOperand(*i))
+            if (consumer->toResumePoint()->isObservableOperand(*i)) {
+                IonSpewDef(IonSpew_Escape, "Array is observable\n", ins);
                 return true;
+            }
             continue;
         }
 
         MDefinition *def = consumer->toDefinition();
         switch (def->op()) {
           case MDefinition::Op_Elements: {
             MOZ_ASSERT(def->toElements()->object() == ins);
             for (MUseIterator i(def->usesBegin()); i != def->usesEnd(); i++) {
@@ -517,26 +538,38 @@ IsArrayEscaped(MInstruction *ins)
                   case MDefinition::Op_LoadElement: {
                     MOZ_ASSERT(access->toLoadElement()->elements() == def);
 
                     // If we need hole checks, then the array cannot be escaped
                     // as the array might refer to the prototype chain to look
                     // for properties, thus it might do additional side-effects
                     // which are not reflected by the alias set, is we are
                     // bailing on holes.
-                    if (access->toLoadElement()->needsHoleCheck())
+                    if (access->toLoadElement()->needsHoleCheck()) {
+                        IonSpewDef(IonSpew_Escape, "Array ", ins);
+                        IonSpewDef(IonSpew_Escape,
+                                   "  has a load element which needs hole check\n", access);
                         return true;
+                    }
 
                     // If the index is not a constant then this index can alias
                     // all others. We do not handle this case.
                     int32_t index;
-                    if (!IndexOf(access, &index))
+                    if (!IndexOf(access, &index)) {
+                        IonSpewDef(IonSpew_Escape, "Array ", ins);
+                        IonSpewDef(IonSpew_Escape,
+                                   "  has a load element with a non-trivial index\n", access);
                         return true;
-                    if (index < 0 || count <= uint32_t(index))
+                    }
+                    if (index < 0 || count <= uint32_t(index)) {
+                        IonSpewDef(IonSpew_Escape, "Array ", ins);
+                        IonSpewDef(IonSpew_Escape,
+                                   "  has a load element with an out-of-bound index\n", access);
                         return true;
+                    }
                     break;
                   }
 
                   case MDefinition::Op_StoreElement: {
                     MOZ_ASSERT(access->toStoreElement()->elements() == def);
 
                     // If we need hole checks, then the array cannot be escaped
                     // as the array might refer to the prototype chain to look
@@ -544,52 +577,66 @@ IsArrayEscaped(MInstruction *ins)
                     // which are not reflected by the alias set, is we are
                     // bailing on holes.
                     if (access->toStoreElement()->needsHoleCheck())
                         return true;
 
                     // If the index is not a constant then this index can alias
                     // all others. We do not handle this case.
                     int32_t index;
-                    if (!IndexOf(access, &index))
+                    if (!IndexOf(access, &index)) {
+                        IonSpewDef(IonSpew_Escape, "Array ", ins);
+                        IonSpewDef(IonSpew_Escape, "  has a store element with a non-trivial index\n", access);
                         return true;
-                    if (index < 0 || count <= uint32_t(index))
+                    }
+                    if (index < 0 || count <= uint32_t(index)) {
+                        IonSpewDef(IonSpew_Escape, "Array ", ins);
+                        IonSpewDef(IonSpew_Escape, "  has a store element with an out-of-bound index\n", access);
                         return true;
+                    }
 
                     // We are not yet encoding magic hole constants in resume points.
-                    if (access->toStoreElement()->value()->type() == MIRType_MagicHole)
+                    if (access->toStoreElement()->value()->type() == MIRType_MagicHole) {
+                        IonSpewDef(IonSpew_Escape, "Array ", ins);
+                        IonSpewDef(IonSpew_Escape, "  has a store element with an magic-hole constant\n", access);
                         return true;
+                    }
                     break;
                   }
 
                   case MDefinition::Op_SetInitializedLength:
                     MOZ_ASSERT(access->toSetInitializedLength()->elements() == def);
                     break;
 
                   case MDefinition::Op_InitializedLength:
                     MOZ_ASSERT(access->toInitializedLength()->elements() == def);
                     break;
 
                   case MDefinition::Op_ArrayLength:
                     MOZ_ASSERT(access->toArrayLength()->elements() == def);
                     break;
 
                   default:
+                    IonSpewDef(IonSpew_Escape, "Array's element ", ins);
+                    IonSpewDef(IonSpew_Escape, "  is escaped by\n", def);
                     return true;
                 }
             }
 
             break;
           }
 
           default:
+            IonSpewDef(IonSpew_Escape, "Array ", ins);
+            IonSpewDef(IonSpew_Escape, "  is escaped by\n", def);
             return true;
         }
     }
 
+    IonSpewDef(IonSpew_Escape, "Array is not escaped\n", ins);
     return false;
 }
 
 struct ArrayTrait {
     typedef MArrayState BlockState;
     typedef Vector<BlockState *, 8, SystemAllocPolicy> GraphState;
 };