Remove bytecode uses analysis, keep track of SSA values that were folded away when building MIR, bug 818869. r=jandem
authorBrian Hackett <bhackett1024@gmail.com>
Mon, 10 Dec 2012 12:02:31 -0700 (2012-12-10)
changeset 115546 8275b86c0b62e2b1f284388af7a4cacc6eaa8ecc
parent 115545 508d1f5c60b050dbd72330204db528a796973229
child 115547 c73e30eaccddedf560d3247a385a48721362a0f7
push id24015
push useremorley@mozilla.com
push dateTue, 11 Dec 2012 15:51:15 +0000 (2012-12-11)
treeherdermozilla-central@87f8165c5a0b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs818869
milestone20.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
Remove bytecode uses analysis, keep track of SSA values that were folded away when building MIR, bug 818869. r=jandem
js/src/ion/IonAnalysis.cpp
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/ion/MIR.h
js/src/ion/TypePolicy.cpp
--- a/js/src/ion/IonAnalysis.cpp
+++ b/js/src/ion/IonAnalysis.cpp
@@ -69,16 +69,22 @@ ion::EliminateDeadResumePointOperands(MI
             // Scanning uses does not give us sufficient information to tell
             // where instructions that are involved in box/unbox operations or
             // parameter passing might be live. Rewriting uses of these terms
             // in resume points may affect the interpreter's behavior. Rather
             // than doing a more sophisticated analysis, just ignore these.
             if (ins->isUnbox() || ins->isParameter())
                 continue;
 
+            // If the instruction's behavior has been constant folded into a
+            // separate instruction, we can't determine precisely where the
+            // instruction becomes dead and can't eliminate its uses.
+            if (ins->isFolded())
+                continue;
+
             // Check if this instruction's result is only used within the
             // current block, and keep track of its last use in a definition
             // (not resume point). This requires the instructions in the block
             // to be numbered, ensured by running this immediately after alias
             // analysis.
             uint32_t maxDefinition = 0;
             for (MUseDefIterator uses(*ins); uses; uses++) {
                 if (uses.def()->block() != *block || uses.def()->isBox() || uses.def()->isPassArg()) {
@@ -114,20 +120,16 @@ ion::EliminateDeadResumePointOperands(MI
                 // cannot be observed. If the undefined value were to flow to,
                 // say, a dead property access the interpreter could throw an
                 // exception; we avoid this problem by removing dead operands
                 // before removing dead code.
                 MConstant *constant = MConstant::New(UndefinedValue());
                 block->insertBefore(*(block->begin()), constant);
                 uses = mrp->replaceOperand(uses, constant);
             }
-
-            MResumePoint *mrp = ins->resumePoint();
-            if (!mrp)
-                continue;
         }
     }
 
     return true;
 }
 
 // Instructions are useless if they are unused and have no side effects.
 // This pass eliminates useless instructions.
@@ -154,19 +156,19 @@ ion::EliminateDeadCode(MIRGenerator *mir
     }
 
     return true;
 }
 
 static inline bool
 IsPhiObservable(MPhi *phi)
 {
-    // If the phi has bytecode uses, there may be no SSA uses but the value
-    // is still observable in the interpreter after a bailout.
-    if (phi->hasBytecodeUses())
+    // If the phi has uses which are not reflected in SSA, then behavior in the
+    // interpreter may be affected by removing the phi.
+    if (phi->isFolded())
         return true;
 
     // Check for any SSA uses. Note that this skips reading resume points,
     // which we don't count as actual uses. If the only uses are resume points,
     // then the SSA name is never consumed by the program.
     for (MUseDefIterator iter(phi); iter; iter++) {
         if (!iter.def()->isPhi())
             return true;
@@ -198,20 +200,19 @@ IsPhiRedundant(MPhi *phi)
 {
     MDefinition *first = phi->getOperand(0);
 
     for (size_t i = 1; i < phi->numOperands(); i++) {
         if (phi->getOperand(i) != first && phi->getOperand(i) != phi)
             return NULL;
     }
 
-    // Propagate the HasBytecodeUses flag if |phi| is replaced with
-    // another phi.
-    if (phi->hasBytecodeUses() && first->isPhi())
-        first->toPhi()->setHasBytecodeUses();
+    // Propagate the Folded flag if |phi| is replaced with another phi.
+    if (phi->isFolded())
+        first->setFoldedUnchecked();
 
     return first;
 }
 
 bool
 ion::EliminatePhis(MIRGenerator *mir, MIRGraph &graph)
 {
     Vector<MPhi *, 16, SystemAllocPolicy> worklist;
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -376,17 +376,17 @@ IonBuilder::processIterators()
         }
     }
 
     // Propagate the iterator and live status of phis to all other connected
     // phis.
     while (!worklist.empty()) {
         MPhi *phi = worklist.popCopy();
         phi->setIterator();
-        phi->setHasBytecodeUses();
+        phi->setFoldedUnchecked();
 
         for (MUseDefIterator iter(phi); iter; iter++) {
             if (iter.def()->isPhi()) {
                 MPhi *other = iter.def()->toPhi();
                 if (!other->isIterator() && !worklist.append(other))
                     return false;
             }
         }
@@ -514,20 +514,22 @@ IonBuilder::rewriteParameters()
 
         JSValueType definiteType = types->getKnownTypeTag();
         if (definiteType == JSVAL_TYPE_UNKNOWN)
             continue;
 
         MInstruction *actual = NULL;
         switch (definiteType) {
           case JSVAL_TYPE_UNDEFINED:
+            param->setFoldedUnchecked();
             actual = MConstant::New(UndefinedValue());
             break;
 
           case JSVAL_TYPE_NULL:
+            param->setFoldedUnchecked();
             actual = MConstant::New(NullValue());
             break;
 
           default:
             actual = MUnbox::New(param, MIRTypeFromValueType(definiteType), MUnbox::Infallible);
             break;
         }
 
@@ -681,17 +683,16 @@ IonBuilder::traverseBytecode()
             if (status == ControlStatus_Error)
                 return false;
             if (!current)
                 return true;
         }
 
         // Nothing in inspectOpcode() is allowed to advance the pc.
         JSOp op = JSOp(*pc);
-        markPhiBytecodeUses(pc);
         if (!inspectOpcode(op))
             return false;
 
         pc += js_CodeSpec[op].length;
 #ifdef TRACK_SNAPSHOTS
         current->updateTrackedPc(pc);
 #endif
     }
@@ -757,29 +758,16 @@ IonBuilder::snoopControlFlow(JSOp op)
         return ControlStatus_Error;
 
       default:
         break;
     }
     return ControlStatus_None;
 }
 
-void
-IonBuilder::markPhiBytecodeUses(jsbytecode *pc)
-{
-    unsigned nuses = analyze::GetUseCount(script_, pc - script_->code);
-    for (unsigned i = 0; i < nuses; i++) {
-        MDefinition *def = current->peek(-int32_t(i + 1));
-        if (def->isPassArg())
-            def = def->toPassArg()->getArgument();
-        if (def->isPhi())
-            def->toPhi()->setHasBytecodeUses();
-    }
-}
-
 bool
 IonBuilder::inspectOpcode(JSOp op)
 {
     AssertCanGC();
 
     // Don't compile fat opcodes, run the decomposed version instead.
     if (js_CodeSpec[op].format & JOF_DECOMPOSE)
         return true;
@@ -4863,19 +4851,21 @@ IonBuilder::pushTypeBarrier(MInstruction
         return true;
     }
 
     if (!observed) {
         JSValueType type = actual->getKnownTypeTag();
         MInstruction *replace = NULL;
         switch (type) {
           case JSVAL_TYPE_UNDEFINED:
+            ins->setFoldedUnchecked();
             replace = MConstant::New(UndefinedValue());
             break;
           case JSVAL_TYPE_NULL:
+            ins->setFoldedUnchecked();
             replace = MConstant::New(NullValue());
             break;
           case JSVAL_TYPE_UNKNOWN:
             break;
           default: {
             MIRType replaceType = MIRTypeFromValueType(type);
             if (ins->type() == MIRType_Value)
                 replace = MUnbox::New(ins, replaceType, MUnbox::Infallible);
@@ -5305,27 +5295,29 @@ IonBuilder::jsop_getelem_dense()
 }
 
 static MInstruction *
 GetTypedArrayLength(MDefinition *obj)
 {
     if (obj->isConstant()) {
         JSObject *array = &obj->toConstant()->value().toObject();
         int32_t length = (int32_t) TypedArray::length(array);
+        obj->setFoldedUnchecked();
         return MConstant::New(Int32Value(length));
     }
     return MTypedArrayLength::New(obj);
 }
 
 static MInstruction *
 GetTypedArrayElements(MDefinition *obj)
 {
     if (obj->isConstant()) {
         JSObject *array = &obj->toConstant()->value().toObject();
         void *data = TypedArray::viewData(array);
+        obj->setFoldedUnchecked();
         return MConstantElements::New(data);
     }
     return MTypedArrayElements::New(obj);
 }
 
 bool
 IonBuilder::jsop_getelem_typed(int arrayType)
 {
@@ -5634,34 +5626,36 @@ IonBuilder::jsop_arguments()
     current->push(lazyArguments_);
     return true;
 }
 
 bool
 IonBuilder::jsop_arguments_length()
 {
     // Type Inference has guaranteed this is an optimized arguments object.
-    current->pop();
+    MDefinition *args = current->pop();
+    args->setFoldedUnchecked();
 
     MInstruction *ins = MArgumentsLength::New();
     current->add(ins);
     current->push(ins);
     return true;
 }
 
 bool
 IonBuilder::jsop_arguments_getelem()
 {
     types::StackTypeSet *barrier = oracle->propertyReadBarrier(script_, pc);
     types::StackTypeSet *types = oracle->propertyRead(script_, pc);
 
     MDefinition *idx = current->pop();
 
     // Type Inference has guaranteed this is an optimized arguments object.
-    current->pop();
+    MDefinition *args = current->pop();
+    args->setFoldedUnchecked();
 
     // To ensure that we are not looking above the number of actual arguments.
     MArgumentsLength *length = MArgumentsLength::New();
     current->add(length);
 
     // Ensure idx is an integer.
     MInstruction *index = MToInt32::New(idx);
     current->add(index);
@@ -6267,21 +6261,23 @@ IonBuilder::getPropTryConstant(bool *emi
         return false;
 
     if (!isConstant)
         return true;
 
     MDefinition *obj = current->pop();
 
     // Property access is a known constant -- safe to emit.
-	JS_ASSERT(!testString || !testObject);
+    JS_ASSERT(!testString || !testObject);
     if (testObject)
         current->add(MGuardObject::New(obj));
-	else if (testString)
+    else if (testString)
         current->add(MGuardString::New(obj));
+    else
+        obj->setFoldedUnchecked();
 
     MConstant *known = MConstant::New(ObjectValue(*singleton));
     if (singleton->isFunction()) {
         RootedFunction singletonFunc(cx, singleton->toFunction());
         if (TestAreKnownDOMTypes(cx, unaryTypes.inTypes) &&
             TestShouldDOMCall(cx, unaryTypes.inTypes, singletonFunc))
         {
             FreezeDOMTypes(cx, unaryTypes.inTypes);
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -186,17 +186,16 @@ class IonBuilder : public MIRGenerator
 
     bool build();
     bool buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint,
                      MDefinition *thisDefn, MDefinitionVector &args);
 
   private:
     bool traverseBytecode();
     ControlStatus snoopControlFlow(JSOp op);
-    void markPhiBytecodeUses(jsbytecode *pc);
     bool processIterators();
     bool inspectOpcode(JSOp op);
     uint32_t readIndex(jsbytecode *pc);
     JSAtom *readAtom(jsbytecode *pc);
     bool abort(const char *message, ...);
     void spew(const char *message);
 
     static bool inliningEnabled() {
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -43,16 +43,17 @@ MIRType MIRTypeFromValue(const js::Value
 #define MIR_FLAG_LIST(_)                                                        \
     _(InWorklist)                                                               \
     _(EmittedAtUses)                                                            \
     _(LoopInvariant)                                                            \
     _(Commutative)                                                              \
     _(Movable)       /* Allow LICM and GVN to move this instruction */          \
     _(Lowered)       /* (Debug only) has a virtual register */                  \
     _(Guard)         /* Not removable if uses == 0 */                           \
+    _(Folded)        /* Has constant folded uses not reflected in SSA */        \
                                                                                 \
     /* The instruction has been marked dead for lazy removal from resume
      * points.
      */                                                                         \
     _(Unused)                                                                   \
     _(DOMFunction)   /* Contains or uses a common DOM method function */
 
 class MDefinition;
@@ -2731,22 +2732,20 @@ class MFromCharCode
     }
 };
 
 class MPhi : public MDefinition, public InlineForwardListNode<MPhi>
 {
     js::Vector<MDefinition *, 2, IonAllocPolicy> inputs_;
     uint32_t slot_;
     bool triedToSpecialize_;
-    bool hasBytecodeUses_;
     bool isIterator_;
     MPhi(uint32_t slot)
       : slot_(slot),
         triedToSpecialize_(false),
-        hasBytecodeUses_(false),
         isIterator_(false)
     {
         setResultType(MIRType_Value);
     }
 
   protected:
     void setOperand(size_t index, MDefinition *operand) {
         inputs_[index] = operand;
@@ -2773,22 +2772,16 @@ class MPhi : public MDefinition, public 
         setResultType(type);
     }
     bool addInput(MDefinition *ins);
 
     MDefinition *foldsTo(bool useValueNumbers);
 
     bool congruentTo(MDefinition * const &ins) const;
 
-    bool hasBytecodeUses() const {
-        return hasBytecodeUses_;
-    }
-    void setHasBytecodeUses() {
-        hasBytecodeUses_ = true;
-    }
     bool isIterator() const {
         return isIterator_;
     }
     void setIterator() {
         isIterator_ = true;
     }
 
     AliasSet getAliasSet() const {
--- a/js/src/ion/TypePolicy.cpp
+++ b/js/src/ion/TypePolicy.cpp
@@ -403,21 +403,23 @@ StoreTypedArrayPolicy::adjustInputs(MIns
     // The conversion is based on TypedArrayTemplate::setElementTail.
     switch (value->type()) {
       case MIRType_Int32:
       case MIRType_Double:
       case MIRType_Boolean:
       case MIRType_Value:
         break;
       case MIRType_Null:
+        value->setFoldedUnchecked();
         value = MConstant::New(Int32Value(0));
         ins->block()->insertBefore(ins, value->toInstruction());
         break;
       case MIRType_Object:
       case MIRType_Undefined:
+        value->setFoldedUnchecked();
         value = MConstant::New(DoubleValue(js_NaN));
         ins->block()->insertBefore(ins, value->toInstruction());
         break;
       case MIRType_String:
         value = boxAt(ins, value);
         break;
       default:
         JS_NOT_REACHED("Unexpected type");