Bug 564581 - TM: avoid js_IdIsIndex() calls on trace. r=brendan.
authorNicholas Nethercote <nnethercote@mozilla.com>
Wed, 12 May 2010 19:34:01 -0700
changeset 42694 48082710c12f379ca6f9c077c7824dec333589e7
parent 42693 744611b4f3d98eea5b51e87c90b2036b5a0ab09e
child 42695 dbeffed4edb0941665fdb3a30e0f9f1908fda3b3
push id13436
push userrsayre@mozilla.com
push dateMon, 24 May 2010 23:31:12 +0000
treeherdermozilla-central@23b20b0eb9f3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbrendan
bugs564581
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
Bug 564581 - TM: avoid js_IdIsIndex() calls on trace. r=brendan.
js/src/jsbuiltins.cpp
js/src/jsbuiltins.h
js/src/jsscope.h
js/src/jsscopeinlines.h
js/src/jstracer.cpp
js/src/jstracer.h
--- a/js/src/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -189,18 +189,19 @@ JS_DEFINE_CALLINFO_2(extern, DOUBLE, js_
 
 int32 FASTCALL
 js_StringToInt32(JSContext* cx, JSString* str)
 {
     return StringToNumberType<int32>(cx, str);
 }
 JS_DEFINE_CALLINFO_2(extern, INT32, js_StringToInt32, CONTEXT, STRING, 1, ACC_NONE)
 
-JSBool FASTCALL
-js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop)
+/* Nb: it's always safe to set isDefinitelyAtom to false if you're unsure or don't know. */
+static inline JSBool
+AddPropertyHelper(JSContext* cx, JSObject* obj, JSScopeProperty* sprop, bool isDefinitelyAtom)
 {
     JS_LOCK_OBJ(cx, obj);
 
     uint32 slot = sprop->slot;
     JSScope* scope = obj->scope();
     if (slot != scope->freeslot)
         goto exit_trace;
     JS_ASSERT(sprop->parent == scope->lastProperty());
@@ -222,17 +223,17 @@ js_AddProperty(JSContext* cx, JSObject* 
                 goto exit_trace;
 
             if (slot != sprop->slot) {
                 js_FreeSlot(cx, obj, slot);
                 goto exit_trace;
             }
         }
 
-        scope->extend(cx, sprop);
+        scope->extend(cx, sprop, isDefinitelyAtom);
     } else {
         JSScopeProperty *sprop2 =
             scope->addProperty(cx, sprop->id, sprop->getter(), sprop->setter(),
                                SPROP_INVALID_SLOT, sprop->attributes(), sprop->getFlags(),
                                sprop->shortid);
         if (sprop2 != sprop)
             goto exit_trace;
     }
@@ -242,18 +243,31 @@ js_AddProperty(JSContext* cx, JSObject* 
 
     JS_UNLOCK_SCOPE(cx, scope);
     return JS_TRUE;
 
   exit_trace:
     JS_UNLOCK_SCOPE(cx, scope);
     return JS_FALSE;
 }
+
+JSBool FASTCALL
+js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop)
+{
+    return AddPropertyHelper(cx, obj, sprop, /* isDefinitelyAtom = */false);
+}
 JS_DEFINE_CALLINFO_3(extern, BOOL, js_AddProperty, CONTEXT, OBJECT, SCOPEPROP, 0, ACC_STORE_ANY)
 
+JSBool FASTCALL
+js_AddAtomProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop)
+{
+    return AddPropertyHelper(cx, obj, sprop, /* isDefinitelyAtom = */true);
+}
+JS_DEFINE_CALLINFO_3(extern, BOOL, js_AddAtomProperty, CONTEXT, OBJECT, SCOPEPROP, 0, ACC_STORE_ANY)
+
 static JSBool
 HasProperty(JSContext* cx, JSObject* obj, jsid id)
 {
     // Check that we know how the lookup op will behave.
     for (JSObject* pobj = obj; pobj; pobj = pobj->getProto()) {
         if (pobj->map->ops->lookupProperty != js_LookupProperty)
             return JSVAL_TO_SPECIAL(JSVAL_VOID);
         JSClass* clasp = pobj->getClass();
--- a/js/src/jsbuiltins.h
+++ b/js/src/jsbuiltins.h
@@ -529,16 +529,17 @@ JS_DECLARE_CALLINFO(js_UnboxInt32)
 JS_DECLARE_CALLINFO(js_TryUnboxInt32)
 JS_DECLARE_CALLINFO(js_dmod)
 JS_DECLARE_CALLINFO(js_imod)
 JS_DECLARE_CALLINFO(js_DoubleToInt32)
 JS_DECLARE_CALLINFO(js_DoubleToUint32)
 JS_DECLARE_CALLINFO(js_StringToNumber)
 JS_DECLARE_CALLINFO(js_StringToInt32)
 JS_DECLARE_CALLINFO(js_AddProperty)
+JS_DECLARE_CALLINFO(js_AddAtomProperty)
 JS_DECLARE_CALLINFO(js_HasNamedProperty)
 JS_DECLARE_CALLINFO(js_HasNamedPropertyInt32)
 JS_DECLARE_CALLINFO(js_TypeOfObject)
 JS_DECLARE_CALLINFO(js_TypeOfBoolean)
 JS_DECLARE_CALLINFO(js_BooleanIntToString)
 JS_DECLARE_CALLINFO(js_NewNullClosure)
 JS_DECLARE_CALLINFO(js_PopInterpFrame)
 JS_DECLARE_CALLINFO(js_ConcatN)
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -264,17 +264,17 @@ struct JSScope : public JSObjectMap
     /* These four inline methods are defined further below in this .h file. */
     inline void setLastProperty(JSScopeProperty *sprop);
     inline void removeLastProperty();
     inline void removeDictionaryProperty(JSScopeProperty *sprop);
     inline void insertDictionaryProperty(JSScopeProperty *sprop, JSScopeProperty **childp);
 
     /* Defined in jsscopeinlines.h to avoid including implementation dependencies here. */
     inline void updateShape(JSContext *cx);
-    inline void updateFlags(const JSScopeProperty *sprop);
+    inline void updateFlags(const JSScopeProperty *sprop, bool isDefinitelyAtom = false);
 
   protected:
     void initMinimal(JSContext *cx, uint32 newShape);
 
   private:
     bool createTable(JSContext *cx, bool report);
     bool changeTable(JSContext *cx, int change);
     void reportReadOnlyScope(JSContext *cx);
@@ -346,17 +346,17 @@ struct JSScope : public JSObjectMap
 
     /* Remove id from this scope. */
     bool removeProperty(JSContext *cx, jsid id);
 
     /* Clear the scope, making it empty. */
     void clear(JSContext *cx);
 
     /* Extend this scope to have sprop as its last-added property. */
-    void extend(JSContext *cx, JSScopeProperty *sprop);
+    void extend(JSContext *cx, JSScopeProperty *sprop, bool isDefinitelyAtom = false);
 
     /*
      * Read barrier to clone a joined function object stored as a method.
      * Defined in jsscopeinlines.h, but not declared inline per standard style
      * in order to avoid gcc warnings.
      */
     bool methodReadBarrier(JSContext *cx, JSScopeProperty *sprop, jsval *vp);
 
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -85,33 +85,33 @@ inline void
 JSScope::updateShape(JSContext *cx)
 {
     JS_ASSERT(object);
     js::LeaveTraceIfGlobalObject(cx, object);
     shape = (hasOwnShape() || !lastProp) ? js_GenerateShape(cx, false) : lastProp->shape;
 }
 
 inline void
-JSScope::updateFlags(const JSScopeProperty *sprop)
+JSScope::updateFlags(const JSScopeProperty *sprop, bool isDefinitelyAtom)
 {
     jsuint index;
-    if (js_IdIsIndex(sprop->id, &index))
+    if (!isDefinitelyAtom && js_IdIsIndex(sprop->id, &index))
         setIndexedProperties();
 
     if (sprop->isMethod())
         setMethodBarrier();
 }
 
 inline void
-JSScope::extend(JSContext *cx, JSScopeProperty *sprop)
+JSScope::extend(JSContext *cx, JSScopeProperty *sprop, bool isDefinitelyAtom)
 {
     ++entryCount;
     setLastProperty(sprop);
     updateShape(cx);
-    updateFlags(sprop);
+    updateFlags(sprop, isDefinitelyAtom);
 }
 
 /*
  * Property read barrier for deferred cloning of compiler-created function
  * objects optimized as typically non-escaping, ad-hoc methods in obj.
  */
 inline bool
 JSScope::methodReadBarrier(JSContext *cx, JSScopeProperty *sprop, jsval *vp)
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -11263,17 +11263,17 @@ MethodWriteBarrier(JSContext* cx, JSObje
 
     return obj->scope()->methodWriteBarrier(cx, sprop, tvr.value());
 }
 JS_DEFINE_CALLINFO_4(static, BOOL_FAIL, MethodWriteBarrier, CONTEXT, OBJECT, SCOPEPROP, OBJECT,
                      0, ACC_STORE_ANY)
 
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::setProp(jsval &l, PropertyCacheEntry* entry, JSScopeProperty* sprop,
-                       jsval &v, LIns*& v_ins)
+                       jsval &v, LIns*& v_ins, bool isDefinitelyAtom)
 {
     if (entry == JS_NO_PROP_CACHE_FILL)
         RETURN_STOP("can't trace uncacheable property set");
     JS_ASSERT_IF(entry->vcapTag() >= 1, !sprop->hasSlot());
     if (!sprop->hasDefaultSetter() && sprop->slot != SPROP_INVALID_SLOT)
         RETURN_STOP("can't trace set of property with setter and slot");
     if (sprop->hasSetterValue())
         RETURN_STOP("can't trace JavaScript function setter");
@@ -11330,17 +11330,18 @@ TraceRecorder::setProp(jsval &l, Propert
 
     // Add a property to the object if necessary.
     if (entry->adding()) {
         JS_ASSERT(sprop->hasSlot());
         if (obj == globalObj)
             RETURN_STOP("adding a property to the global object");
 
         LIns* args[] = { INS_CONSTSPROP(sprop), obj_ins, cx_ins };
-        LIns* ok_ins = lir->insCall(&js_AddProperty_ci, args);
+        const CallInfo *ci = isDefinitelyAtom ? &js_AddAtomProperty_ci : &js_AddProperty_ci;
+        LIns* ok_ins = lir->insCall(ci, args);
         guard(false, lir->insEqI_0(ok_ins), OOM_EXIT);
     }
 
     return nativeSet(obj, obj_ins, sprop, v, v_ins);
 }
 
 JS_REQUIRES_STACK RecordingStatus
 TraceRecorder::setUpwardTrackedVar(jsval* stackVp, jsval v, LIns* v_ins)
@@ -11490,19 +11491,22 @@ TraceRecorder::setCallProp(JSObject *cal
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_SetPropHit(PropertyCacheEntry* entry, JSScopeProperty* sprop)
 {
     jsval& r = stackval(-1);
     jsval& l = stackval(-2);
     LIns* v_ins;
-    CHECK_STATUS_A(setProp(l, entry, sprop, r, v_ins));
 
     jsbytecode* pc = cx->fp->regs->pc;
+
+    bool isDefinitelyAtom = (*pc == JSOP_SETPROP);
+    CHECK_STATUS_A(setProp(l, entry, sprop, r, v_ins, isDefinitelyAtom));
+
     switch (*pc) {
       case JSOP_SETPROP:
       case JSOP_SETNAME:
       case JSOP_SETMETHOD:
         if (pc[JSOP_SETPROP_LENGTH] != JSOP_POP)
             set(&l, v_ins);
         break;
 
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -1306,17 +1306,18 @@ class TraceRecorder
                                                                   nanojit::LIns* obj_ins,
                                                                   JSScopeProperty* sprop);
 
     JS_REQUIRES_STACK RecordingStatus nativeSet(JSObject* obj, nanojit::LIns* obj_ins,
                                                   JSScopeProperty* sprop,
                                                   jsval v, nanojit::LIns* v_ins);
     JS_REQUIRES_STACK RecordingStatus setProp(jsval &l, PropertyCacheEntry* entry,
                                                 JSScopeProperty* sprop,
-                                                jsval &v, nanojit::LIns*& v_ins);
+                                                jsval &v, nanojit::LIns*& v_ins,
+                                                bool isDefinitelyAtom);
     JS_REQUIRES_STACK RecordingStatus setCallProp(JSObject *callobj, nanojit::LIns *callobj_ins,
                                                     JSScopeProperty *sprop, nanojit::LIns *v_ins,
                                                     jsval v);
     JS_REQUIRES_STACK RecordingStatus initOrSetPropertyByName(nanojit::LIns* obj_ins,
                                                                 jsval* idvalp, jsval* rvalp,
                                                                 bool init);
     JS_REQUIRES_STACK RecordingStatus initOrSetPropertyByIndex(nanojit::LIns* obj_ins,
                                                                  nanojit::LIns* index_ins,