Bug 620640 - Allow there to be more than 1 pending global slot to set (r=jorendorff)
authorLuke Wagner <lw@mozilla.com>
Tue, 11 Jan 2011 15:19:57 -0800
changeset 60559 18e064a4bb68b6de1337a25ee13b98e9940a967c
parent 60558 039f81de26e57580d52004ad7203f246cabd24d6
child 60560 cb17d7d6a83197dcc20a747ffed83cc00f6cd64f
push id18037
push usercleary@mozilla.com
push dateFri, 14 Jan 2011 17:42:55 +0000
treeherdermozilla-central@4e0501a0c5e5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs620640
milestone2.0b9pre
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 620640 - Allow there to be more than 1 pending global slot to set (r=jorendorff)
js/src/jit-test/tests/basic/testMultiplePendingGlobalWrites.js
js/src/jstl.h
js/src/jstracer.cpp
js/src/jstracer.h
js/src/jsvector.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testMultiplePendingGlobalWrites.js
@@ -0,0 +1,11 @@
+var a, b;
+function g(x) {
+    var y = x++;
+    return [x, y];
+}
+function f() {
+    for(var i=0; i<20; i++) {
+        [a,b] = g("10");
+    }
+}
+f();
--- a/js/src/jstl.h
+++ b/js/src/jstl.h
@@ -430,16 +430,34 @@ Reverse(T *beg, T *end)
             return;
         T tmp = *beg;
         *beg = *end;
         *end = tmp;
         ++beg;
     }
 }
 
+template <class T>
+static inline T *
+Find(T *beg, T *end, const T &v)
+{
+    for (T *p = beg; p != end; ++p) {
+        if (*p == v)
+            return p;
+    }
+    return end;
+}
+
+template <class Container>
+static inline typename Container::ElementType *
+Find(Container &c, const typename Container::ElementType &v)
+{
+    return Find(c.begin(), c.end(), v);
+}
+
 template <typename InputIterT, typename CallableT>
 void
 ForEach(InputIterT begin, InputIterT end, CallableT f)
 {
     for (; begin != end; ++begin)
         f(*begin);
 }
 
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -2236,17 +2236,17 @@ TraceRecorder::TraceRecorder(JSContext* 
     initDepth(0),
     hadNewInit(false),
     rval_ins(NULL),
     native_rval_ins(NULL),
     newobj_ins(NULL),
     pendingSpecializedNative(NULL),
     pendingUnboxSlot(NULL),
     pendingGuardCondition(NULL),
-    pendingGlobalSlotToSet(-1),
+    pendingGlobalSlotsToSet(cx),
     pendingLoop(true),
     generatedSpecializedNative(),
     tempTypeMap(cx),
     w(&tempAlloc(), lirbuf)
 {
     JS_ASSERT(globalObj == cx->fp()->scopeChain().getGlobal());
     JS_ASSERT(globalObj->hasOwnShape());
     JS_ASSERT(cx->regs->pc == (jsbytecode*)fragment->ip);
@@ -3705,18 +3705,18 @@ TraceRecorder::writeBack(LIns* ins, LIns
     if (shouldDemoteToInt32 && IsPromotedInt32(ins))
         ins = w.demoteToInt32(ins);
 
     Address addr;
     if (base == lirbuf->sp) {
         addr = StackAddress(base, offset);
     } else {
         addr = EosAddress(base, offset);
-        JS_ASSERT(pendingGlobalSlotToSet == -1);
-        pendingGlobalSlotToSet = offset / sizeof(double);
+        unsigned slot = unsigned(offset / sizeof(double));
+        (void)pendingGlobalSlotsToSet.append(slot);  /* OOM is safe. */
     }
     return w.st(ins, addr);
 }
 
 /* Update the tracker, then issue a write back store. */
 JS_REQUIRES_STACK void
 TraceRecorder::setImpl(void* p, LIns* i, bool shouldDemoteToInt32)
 {
@@ -7209,17 +7209,17 @@ TraceRecorder::monitorRecording(JSOp op)
     JS_ASSERT(!fragment->lastIns);
 
     /*
      * Clear one-shot state used to communicate between record_JSOP_CALL and post-
      * opcode-case-guts record hook (record_NativeCallComplete).
      */
     pendingSpecializedNative = NULL;
     newobj_ins = NULL;
-    pendingGlobalSlotToSet = -1;
+    pendingGlobalSlotsToSet.clear();
 
     /* Handle one-shot request from finishGetProp or INSTANCEOF to snapshot post-op state and guard. */
     if (pendingGuardCondition) {
         LIns* cond = pendingGuardCondition;
         bool expected = true;
         /* Put 'cond' in a form suitable for a guard/branch condition if it's not already. */
         ensureCond(&cond, &expected);
         guard(expected, cond, STATUS_EXIT);
@@ -10168,17 +10168,17 @@ TraceRecorder::guardNativeConversion(Val
         // We could specialize to guard on just JSClass.convert, but a mere
         // class guard is simpler and slightly faster.
         guardClass(obj_ins, obj->getClass(), snapshot(MISMATCH_EXIT), LOAD_NORMAL);
     }
     return RECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK void
-TraceRecorder::clearReturningFrameFromNativeveTracker()
+TraceRecorder::clearReturningFrameFromNativeTracker()
 {
     /*
      * Clear all tracker entries associated with the frame for the same reason
      * described in record_EnterFrame. Reuse the generic visitor to avoid
      * duplicating logic. The generic visitor stops at 'sp', whereas we need to
      * clear up to script->nslots, so finish the job manually.
      */
     ClearSlotsVisitor visitor(nativeFrameTracker);
@@ -10474,17 +10474,17 @@ TraceRecorder::record_JSOP_RETURN()
         rval_ins = get(&rval);
     }
     debug_only_stmt(JSAutoByteString funBytes);
     debug_only_printf(LC_TMTracer,
                       "returning from %s\n",
                       fp->fun()->atom ?
                         js_AtomToPrintableString(cx, fp->fun()->atom, &funBytes) :
                         "<anonymous>");
-    clearReturningFrameFromNativeveTracker();
+    clearReturningFrameFromNativeTracker();
 
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_GOTO()
 {
     /*
@@ -15825,17 +15825,17 @@ TraceRecorder::record_JSOP_STOP()
      * NB: we do not support script rval (eval, API users who want the result
      * of the last expression-statement, debugger API calls).
      */
     if (fp->isConstructing()) {
         rval_ins = get(&fp->thisValue());
     } else {
         rval_ins = w.immiUndefined();
     }
-    clearReturningFrameFromNativeveTracker();
+    clearReturningFrameFromNativeTracker();
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_GETXPROP()
 {
     Value& l = stackval(-1);
     if (l.isPrimitive())
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -1112,17 +1112,17 @@ class TraceRecorder
 
     /* Carry whether this is a jsval on the native stack from finishGetProp to monitorRecording. */
     Value*                          pendingUnboxSlot;
 
     /* Carry a guard condition to the beginning of the next monitorRecording. */
     nanojit::LIns*                  pendingGuardCondition;
 
     /* See AbortRecordingIfUnexpectedGlobalWrite. */
-    int                             pendingGlobalSlotToSet;
+    js::Vector<unsigned>            pendingGlobalSlotsToSet;
 
     /* Carry whether we have an always-exit from emitIf to checkTraceEnd. */
     bool                            pendingLoop;
 
     /* Temporary JSSpecializedNative used to describe non-specialized fast natives. */
     JSSpecializedNative             generatedSpecializedNative;
 
     /* Temporary JSValueType array used to construct temporary typemaps. */
@@ -1474,17 +1474,17 @@ class TraceRecorder
     JS_REQUIRES_STACK void guardDenseArray(nanojit::LIns* obj_ins, VMSideExit* exit);
     JS_REQUIRES_STACK bool guardHasPrototype(JSObject* obj, nanojit::LIns* obj_ins,
                                              JSObject** pobj, nanojit::LIns** pobj_ins,
                                              VMSideExit* exit);
     JS_REQUIRES_STACK RecordingStatus guardPrototypeHasNoIndexedProperties(JSObject* obj,
                                                                            nanojit::LIns* obj_ins,
                                                                            VMSideExit *exit);
     JS_REQUIRES_STACK RecordingStatus guardNativeConversion(Value& v);
-    JS_REQUIRES_STACK void clearReturningFrameFromNativeveTracker();
+    JS_REQUIRES_STACK void clearReturningFrameFromNativeTracker();
     JS_REQUIRES_STACK void putActivationObjects();
     JS_REQUIRES_STACK RecordingStatus guardCallee(Value& callee);
     JS_REQUIRES_STACK JSStackFrame      *guardArguments(JSObject *obj, nanojit::LIns* obj_ins,
                                                         unsigned *depthp);
     JS_REQUIRES_STACK nanojit::LIns* guardArgsLengthNotAssigned(nanojit::LIns* argsobj_ins);
     JS_REQUIRES_STACK void guardNotHole(nanojit::LIns *argsobj_ins, nanojit::LIns *ids_ins);
     JS_REQUIRES_STACK RecordingStatus getClassPrototype(JSObject* ctor,
                                                           nanojit::LIns*& proto_ins);
@@ -1610,17 +1610,18 @@ class TraceRecorder
     JS_REQUIRES_STACK AbortableRecordingStatus record_SetPropHit(PropertyCacheEntry* entry,
                                                                  const js::Shape* shape);
     JS_REQUIRES_STACK AbortableRecordingStatus record_DefLocalFunSetSlot(uint32 slot,
                                                                          JSObject* obj);
     JS_REQUIRES_STACK AbortableRecordingStatus record_NativeCallComplete();
     void forgetGuardedShapesForObject(JSObject* obj);
 
     bool globalSetExpected(unsigned slot) {
-        if (pendingGlobalSlotToSet != (int)slot) {
+        unsigned *pi = Find(pendingGlobalSlotsToSet, slot);
+        if (pi == pendingGlobalSlotsToSet.end()) {
             /*
              * Do slot arithmetic manually to avoid getSlotRef assertions which
              * do not need to be satisfied for this purpose.
              */
             Value *vp = globalObj->getSlots() + slot;
 
             /* If this global is definitely being tracked, then the write is unexpected. */
             if (tracker.has(vp))
@@ -1630,17 +1631,17 @@ class TraceRecorder
              * Otherwise, only abort if the global is not present in the
              * import typemap. Just deep aborting false here is not acceptable,
              * because the recorder does not guard on every operation that
              * could lazily resolve. Since resolving adds properties to
              * reserved slots, the tracer will never have imported them.
              */
             return tree->globalSlots->offsetOf((uint16)nativeGlobalSlot(vp)) == -1;
         }
-        pendingGlobalSlotToSet = -1;
+        pendingGlobalSlotsToSet.erase(pi);
         return true;
     }
 
 #ifdef DEBUG
     /* Debug printing functionality to emit printf() on trace. */
     JS_REQUIRES_STACK void tprint(const char *format, int count, nanojit::LIns *insa[]);
     JS_REQUIRES_STACK void tprint(const char *format);
     JS_REQUIRES_STACK void tprint(const char *format, nanojit::LIns *ins);
@@ -1873,19 +1874,19 @@ struct TraceVisStateObj {
 #endif /* !JS_TRACER */
 
 namespace js {
 
 /*
  * While recording, the slots of the global object may change payload or type.
  * This is fine as long as the recorder expects this change (and therefore has
  * generated the corresponding LIR, snapshots, etc). The recorder indicates
- * that it expects a write to a global slot by setting pendingGlobalSlotToSet
+ * that it expects a write to a global slot by setting pendingGlobalSlotsToSet
  * in the recorder, before the write is made by the interpreter, and clearing
- * pendingGlobalSlotToSet before recording the next op. Any global slot write
+ * pendingGlobalSlotsToSet before recording the next op. Any global slot write
  * that has not been whitelisted in this manner is therefore unexpected and, if
  * the global slot is actually being tracked, recording must be aborted.
  */
 static JS_INLINE void
 AbortRecordingIfUnexpectedGlobalWrite(JSContext *cx, JSObject *obj, unsigned slot)
 {
 #ifdef JS_TRACER
     if (TraceRecorder *tr = TRACE_RECORDER(cx)) {
--- a/js/src/jsvector.h
+++ b/js/src/jsvector.h
@@ -255,16 +255,18 @@ class Vector : AllocPolicy
         return mBegin + mLength;
     }
 
     const T *endNoCheck() const {
         return mBegin + mLength;
     }
 
   public:
+    typedef T ElementType;
+
     Vector(AllocPolicy = AllocPolicy());
     ~Vector();
 
     /* accessors */
 
     const AllocPolicy &allocPolicy() const {
         return *this;
     }