Add IsConstructing and thisObject to Ion frames (Bug 744253, r=dvander)
authorNicolas Pierron <nicolas.b.pierron@mozilla.com>
Tue, 01 May 2012 17:40:31 -0700
changeset 112441 f39ac9c99046d00a17ae006ae3446952e38d4edb
parent 112440 04590b9cde4e7904b70f98182f4ef69c5710a59d
child 112442 1161f2520e9bdde60321a225084ac126f5173056
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs744253
milestone15.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
Add IsConstructing and thisObject to Ion frames (Bug 744253, r=dvander)
js/src/ion/Ion.cpp
js/src/ion/IonFrameIterator.h
js/src/ion/IonFrames.cpp
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsinfer.cpp
js/src/jsobj.cpp
js/src/vm/Stack.cpp
js/src/vm/Stack.h
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -1017,17 +1017,17 @@ ion::SideCannon(JSContext *cx, StackFram
 
 static void
 InvalidateActivation(FreeOp *fop, uint8 *ionTop, bool invalidateAll)
 {
     IonSpew(IonSpew_Invalidate, "BEGIN invalidating activation");
 
     size_t frameno = 1;
 
-    for (IonFrameIterator it(ionTop); it.more(); ++it, ++frameno) {
+    for (IonFrameIterator it(ionTop); !it.done(); ++it, ++frameno) {
         JS_ASSERT_IF(frameno == 1, it.type() == IonFrame_Exit);
 
 #ifdef DEBUG
         switch (it.type()) {
           case IonFrame_Exit:
             IonSpew(IonSpew_Invalidate, "#%d exit frame @ %p", frameno, it.fp());
             break;
           case IonFrame_JS:
--- a/js/src/ion/IonFrameIterator.h
+++ b/js/src/ion/IonFrameIterator.h
@@ -108,16 +108,18 @@ class IonFrameIterator
     bool isScripted() const {
         return type_ == IonFrame_JS;
     }
     bool isEntry() const {
         return type_ == IonFrame_Entry;
     }
     bool isFunctionFrame() const;
 
+    bool isConstructing(IonActivation *activation) const;
+
     void *calleeToken() const;
     JSFunction *callee() const;
     JSFunction *maybeCallee() const;
     JSScript *script() const;
 
     // Returns the return address of the frame above this one (that is, the
     // return address that returns back to the current frame).
     uint8 *returnAddressToFp() const {
@@ -130,18 +132,18 @@ class IonFrameIterator
     uint8 *prevFp() const;
 
     // Returns the stack space used by the current frame, in bytes. This does
     // not include the size of its fixed header.
     inline size_t frameSize() const;
 
     // Functions used to iterate on frames. When prevType is IonFrame_Entry,
     // the current frame is the last frame.
-    inline bool more() const {
-        return type_ != IonFrame_Entry;
+    inline bool done() const {
+        return type_ == IonFrame_Entry;
     }
     IonFrameIterator &operator++();
 
     // Returns the IonScript associated with this JS frame.
     IonScript *ionScript() const;
 
     // Returns the Safepoint associated with this JS frame. Incurs a lookup
     // overhead.
@@ -160,17 +162,17 @@ class IonActivationIterator
     IonActivation *activation_;
 
   public:
     IonActivationIterator(JSContext *cx);
     IonActivationIterator(JSRuntime *rt);
 
     IonActivationIterator &operator++();
 
-    IonActivation *activation() {
+    IonActivation *activation() const {
         return activation_;
     }
     uint8 *top() const {
         return top_;
     }
     bool more() const;
 };
 
@@ -235,16 +237,18 @@ class InlineFrameIterator
     }
     jsbytecode *pc() const {
         return pc_;
     }
     SnapshotIterator snapshotIterator() const {
         return si_;
     }
     bool isFunctionFrame() const;
+    bool isConstructing(IonActivation *activation) const;
+    JSObject *thisObject() const;
     InlineFrameIterator operator++();
 };
 
 } // namespace ion
 } // namespace js
 
 #endif // jsion_frames_iterator_h__
 
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -446,17 +446,17 @@ MarkIonJSFrame(JSTracer *trc, const IonF
         Value *v = (Value *)layout->slotRef(slot);
         gc::MarkValueRoot(trc, v, "ion-gc-slot");
     }
 }
 
 static void
 MarkIonActivation(JSTracer *trc, uint8 *top)
 {
-    for (IonFrameIterator frames(top); frames.more(); ++frames) {
+    for (IonFrameIterator frames(top); !frames.done(); ++frames) {
         switch (frames.type()) {
           case IonFrame_Exit:
             // The exit frame gets ignored.
             break;
           case IonFrame_JS:
             MarkIonJSFrame(trc, frames);
             break;
           case IonFrame_Rectifier:
@@ -725,8 +725,62 @@ MachineState::FromBailout(uintptr_t regs
     for (unsigned i = 0; i < Registers::Total; i++)
         machine.setRegisterLocation(Register::FromCode(i), &regs[i]);
     for (unsigned i = 0; i < FloatRegisters::Total; i++)
         machine.setRegisterLocation(FloatRegister::FromCode(i), &fpregs[i]);
 
     return machine;
 }
 
+bool
+InlineFrameIterator::isConstructing(IonActivation *activation) const
+{
+    // Skip the current frame and look at the caller's.
+    if (more()) {
+        InlineFrameIterator parent(*this);
+        ++parent;
+
+        // In the case of a JS frame, look up the pc from the snapshot.
+        JS_ASSERT(js_CodeSpec[*parent.pc()].format & JOF_INVOKE);
+
+        return (JSOp)*parent.pc() == JSOP_NEW;
+    }
+
+    return frame_->isConstructing(activation);
+}
+
+bool
+IonFrameIterator::isConstructing(IonActivation *activation) const
+{
+    IonFrameIterator parent(*this);
+
+    // Skip the current frame and look at the caller's.
+    do {
+        ++parent;
+    } while (!parent.done() && !parent.isScripted());
+
+    if (parent.isScripted()) {
+        // In the case of a JS frame, look up the pc from the snapshot.
+        InlineFrameIterator inlinedParent(&parent);
+        JS_ASSERT(js_CodeSpec[*inlinedParent.pc()].format & JOF_INVOKE);
+
+        return (JSOp)*inlinedParent.pc() == JSOP_NEW;
+    }
+
+    JS_ASSERT(parent.done());
+    return activation->entryfp()->isConstructing();
+}
+
+JSObject *
+InlineFrameIterator::thisObject() const
+{
+    // JS_ASSERT(isConstructing(...));
+    SnapshotIterator s(si_);
+
+    // scopeChain
+    s.skip();
+
+    // In strict modes, |this| may not be an object and thus may not be
+    // readable which can either segv in read or trigger the assertion.
+    Value v = s.read();
+    JS_ASSERT(v.isObject());
+    return &v.toObject();
+}
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -153,19 +153,16 @@ fun_getProperty(JSContext *cx, JSObject 
 
         if (!argsobj)
             return false;
 
         *vp = ObjectValue(*argsobj);
         return true;
     }
 
-    StackIter prev(iter);
-    ++prev;
-
 #ifdef JS_METHODJIT
     if (JSID_IS_ATOM(id, cx->runtime->atomState.callerAtom) && fp && fp->prev()) {
         /*
          * If the frame was called from within an inlined frame, mark the
          * innermost function as uninlineable to expand its frame and allow us
          * to recover its callee object.
          */
         JSInlinedSite *inlined;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1114,17 +1114,17 @@ MarkRangeConservativelyAndSkipIon(JSTrac
     const uintptr_t *i = begin;
 
 #if JS_STACK_GROWTH_DIRECTION < 0 && defined(JS_ION)
     // Walk only regions in between Ion activations. Note that non-volatile
     // registers are spilled to the stack before the entry Ion frame, ensuring
     // that the conservative scanner will still see them.
     for (ion::IonActivationIterator ion(rt); ion.more(); ++ion) {
         ion::IonFrameIterator frames(ion.top());
-        while (frames.more())
+        while (!frames.done())
             ++frames;
 
         uintptr_t *ionMin = (uintptr_t *)ion.top();
         uintptr_t *ionEnd = (uintptr_t *)frames.fp();
 
         MarkRangeConservatively(trc, i, ionMin);
         i = ionEnd;
     }
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -3077,21 +3077,20 @@ TypeObject::clearNewScript(JSContext *cx
      * script keeps track of where each property is initialized so we can walk
      * the stack and fix up any such objects.
      */
     Vector<uint32_t, 32> pcOffsets(cx);
     for (ScriptFrameIter iter(cx); !iter.done(); ++iter) {
         pcOffsets.append(uint32_t(iter.pc() - iter.script()->code));
         if (iter.isConstructing() &&
             iter.callee() == newScript->fun &&
-            iter.thisv().isObject() &&
-            !iter.thisv().toObject().hasLazyType() &&
-            iter.thisv().toObject().type() == this)
+            !iter.thisObject()->hasLazyType() &&
+            iter.thisObject()->type() == this)
         {
-            JSObject *obj = &iter.thisv().toObject();
+            JSObject *obj = iter.thisObject();
 
             /* Whether all identified 'new' properties have been initialized. */
             bool finished = false;
 
             /* If not finished, number of properties that have been added. */
             uint32_t numProperties = 0;
 
             /*
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -6302,16 +6302,16 @@ js_DumpBacktrace(JSContext *cx)
     Sprinter sprinter(cx);
     sprinter.init();
     size_t depth = 0;
     for (StackIter i(cx); !i.done(); ++i, ++depth) {
         if (i.isScript()) {
             const char *filename = JS_GetScriptFilename(cx, i.script());
             unsigned line = JS_PCToLineNumber(cx, i.script(), i.pc());
             sprinter.printf("#%d %14p   %s:%d (%p @ %d)\n",
-                            depth, i.fp(), filename, line,
+                            depth, (i.isIon() ? 0 : i.fp()), filename, line,
                             i.script(), i.pc() - i.script()->code);
         } else {
             sprinter.printf("#%d ???\n", depth);
         }
     }
     fprintf(stdout, "%s", sprinter.string());
 }
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1171,20 +1171,20 @@ StackIter::settleOnNewState()
                 popFrame();
                 continue;
             }
 
 #ifdef JS_ION
             if (fp_->runningInIon()) {
                 ionFrames_ = ion::IonFrameIterator(ionActivations_.top());
 
-                while (ionFrames_.more() && !ionFrames_.isScripted())
+                while (!ionFrames_.done() && !ionFrames_.isScripted())
                     ++ionFrames_;
 
-                if (!ionFrames_.more()) {
+                if (ionFrames_.done()) {
                     // In this case, we bailed out the last frame, so we
                     // shouldn't really transition to Ion code.
                     ++ionActivations_;
                     popFrame();
                     continue;
                 }
 
                 state_ = ION;
@@ -1268,19 +1268,17 @@ StackIter::StackIter(JSContext *cx, Save
     savedOption_(savedOption)
 #ifdef JS_ION
     , ionActivations_(cx),
     ionFrames_((uint8_t *)NULL),
     ionInlineFrames_(NULL)
 #endif
 {
 #ifdef JS_METHODJIT
-    CompartmentVector &v = cx->runtime->compartments;
-    for (size_t i = 0; i < v.length(); i++)
-        mjit::ExpandInlineFrames(v[i]);
+    mjit::ExpandInlineFrames(cx->compartment);
 #endif
 
     if (StackSegment *seg = cx->stack.seg_) {
         startOnSegment(seg);
         settleOnNewState();
     } else {
         state_ = DONE;
     }
@@ -1293,20 +1291,20 @@ StackIter::popIonFrame()
     // Keep fp which describes all ion frames.
     poisonRegs();
     if (ionInlineFrames_.more()) {
         ++ionInlineFrames_;
         pc_ = ionInlineFrames_.pc();
         script_ = ionInlineFrames_.script();
     } else {
         ++ionFrames_;
-        while (ionFrames_.more() && !ionFrames_.isScripted())
+        while (!ionFrames_.done() && !ionFrames_.isScripted())
             ++ionFrames_;
 
-        if (ionFrames_.more()) {
+        if (!ionFrames_.done()) {
             ionInlineFrames_ = ion::InlineFrameIterator(&ionFrames_);
             pc_ = ionInlineFrames_.pc();
             script_ = ionInlineFrames_.script();
         } else {
             ++ionActivations_;
             popFrame();
             settleOnNewState();
         }
@@ -1405,16 +1403,18 @@ StackIter::isNonEvalFunctionFrame() cons
 
 bool
 StackIter::isConstructing() const
 {
     switch (state_) {
       case DONE:
         JS_NOT_REACHED("Unexpected state");
         return false;
+      case ION:
+        return ionInlineFrames_.isConstructing(ionActivations_.activation());
       case SCRIPTED:
       case NATIVE:
       case IMPLICIT_NATIVE:
         return fp()->isConstructing();
     }
     return false;
 }
 
@@ -1451,30 +1451,31 @@ StackIter::calleev() const
       case NATIVE:
       case IMPLICIT_NATIVE:
         return nativeArgs().calleev();
     }
     JS_NOT_REACHED("Unexpected state");
     return Value();
 }
 
-Value
-StackIter::thisv() const
+JSObject *
+StackIter::thisObject() const
 {
     switch (state_) {
       case DONE:
-        MOZ_NOT_REACHED("Unexpected state");
-        return Value();
+        break;
+      case ION:
+        return ionInlineFrames_.thisObject();
       case SCRIPTED:
       case NATIVE:
       case IMPLICIT_NATIVE:
-        return fp()->thisValue();
+        return &fp()->thisValue().toObject();
     }
-    MOZ_NOT_REACHED("unexpected state");
-    return Value();
+    JS_NOT_REACHED("Unexpected state");
+    return NULL;
 }
 
 /*****************************************************************************/
 
 AllFramesIter::AllFramesIter(StackSpace &space)
   : seg_(space.seg_),
     fp_(seg_ ? seg_->maybefp() : NULL)
 {
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1833,21 +1833,17 @@ class StackIter
 {
     friend class ContextStack;
     JSContext    *cx_;
   public:
     enum SavedOption { STOP_AT_SAVED, GO_THROUGH_SAVED };
   private:
     SavedOption  savedOption_;
 
-    enum State { DONE, SCRIPTED, NATIVE, IMPLICIT_NATIVE
-#ifdef JS_ION
-        , ION
-#endif
-    };
+    enum State { DONE, SCRIPTED, NATIVE, IMPLICIT_NATIVE, ION };
 
     State        state_;
 
     StackFrame   *fp_;
     CallArgsList *calls_;
 
     StackSegment *seg_;
     Value        *sp_;
@@ -1876,37 +1872,39 @@ class StackIter
 
     bool done() const { return state_ == DONE; }
     StackIter &operator++();
 
     bool operator==(const StackIter &rhs) const;
     bool operator!=(const StackIter &rhs) const { return !(*this == rhs); }
 
     bool isScript() const { JS_ASSERT(!done()); return state_ == SCRIPTED || state_ == ION; }
+    bool isIon() const { JS_ASSERT(!done()); return state_ == ION; }
     bool isImplicitNativeCall() const {
         JS_ASSERT(!done());
         return state_ == IMPLICIT_NATIVE;
     }
     bool isNativeCall() const {
         JS_ASSERT(!done());
         return state_ == NATIVE || state_ == IMPLICIT_NATIVE;
     }
 
     bool isFunctionFrame() const;
     bool isEvalFrame() const;
     bool isNonEvalFunctionFrame() const;
     bool isConstructing() const;
 
+    // :TODO: Add && !isIon() in JS_ASSERT of fp() and sp().
     StackFrame *fp() const { JS_ASSERT(isScript()); return fp_; }
     Value      *sp() const { JS_ASSERT(isScript()); return sp_; }
     jsbytecode *pc() const { JS_ASSERT(isScript()); return pc_; }
     JSScript   *script() const { JS_ASSERT(isScript()); return script_; }
     JSFunction *callee() const;
     Value       calleev() const;
-    Value       thisv() const;
+    JSObject   *thisObject() const;
 
     CallArgs nativeArgs() const { JS_ASSERT(isNativeCall()); return args_; }
 };
 
 /* A filtering of the StackIter to only stop at scripts. */
 class ScriptFrameIter : public StackIter
 {
     void settle() {