Bug 801839 - Clean up jsop_getprop(). r=pierron
☠☠ backed out by 55d64487e54a ☠ ☠
authorSean Stangl <sstangl@mozilla.com>
Tue, 16 Oct 2012 12:07:17 -0700
changeset 110576 73f735acc3e1ee32b5f2e5c3017cbd8defb0d51c
parent 110556 ed39040f53ad3057e8af7b3a7888255dea70bb9d
child 110577 9ba7d327ce675fca4d353c36fc0be10cdba8237e
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewerspierron
bugs801839
milestone19.0a1
Bug 801839 - Clean up jsop_getprop(). r=pierron
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -5800,191 +5800,289 @@ IonBuilder::storeSlot(MDefinition *obj, 
     if (needsBarrier)
         store->setNeedsBarrier();
     return resumeAfter(store);
 }
 
 bool
 IonBuilder::jsop_getprop(HandlePropertyName name)
 {
-    LazyArgumentsType isArguments = oracle->propertyReadMagicArguments(script_, pc);
-    if (isArguments == MaybeArguments)
-        return abort("Type is not definitely lazy arguments.");
-    if (isArguments == DefinitelyArguments) {
-        if (JSOp(*pc) == JSOP_LENGTH)
-            return jsop_arguments_length();
-        // Can also be a callee.
-    }
-
+    RootedId id(cx, NameToId(name));
     MDefinition *obj = current->pop();
-    MInstruction *ins;
 
     types::StackTypeSet *barrier = oracle->propertyReadBarrier(script_, pc);
     types::StackTypeSet *types = oracle->propertyRead(script_, pc);
-
     TypeOracle::Unary unary = oracle->unaryOp(script_, pc);
-    TypeOracle::UnaryTypes unaryTypes = oracle->unaryTypes(script_, pc);
-
-    RootedId id(cx, NameToId(name));
-
+    TypeOracle::UnaryTypes uTypes = oracle->unaryTypes(script_, pc);
+
+    bool emitted = false;
+
+    // Try to optimize arguments.length.
+    if (!getPropTryArgumentsLength(&emitted) || emitted)
+        return emitted;
+
+    // Try to hardcode known constants.
+    if (!getPropTryConstant(&emitted, obj, id, barrier, types, uTypes) || emitted)
+        return emitted;
+
+    // Try to emit loads from definite slots.
+    if (!getPropTryDefiniteSlot(&emitted, obj, name, barrier, types, unary, uTypes) || emitted)
+        return emitted;
+
+    // Try to inline a common property getter, or make a call.
+    if (!getPropTryCommonGetter(&emitted, obj, id, barrier, types, uTypes) || emitted)
+        return emitted;
+
+    // Try to emit a monomorphic cache based on data in JM caches.
+    if (!getPropTryMonomorphic(&emitted, obj, id, barrier, unary, uTypes) || emitted)
+        return emitted;
+
+    // Try to emit a polymorphic cache.
+    if (!getPropTryPolymorphic(&emitted, obj, name, id, barrier, types, unary, uTypes) || emitted)
+        return emitted;
+
+    // Emit a call.
+    MCallGetProperty *call = MCallGetProperty::New(obj, name);
+    current->add(call);
+    current->push(call);
+    if (!resumeAfter(call))
+        return false;
+
+    monitorResult(call, barrier, types);
+    return pushTypeBarrier(call, types, barrier);
+}
+
+bool
+IonBuilder::getPropTryArgumentsLength(bool *emitted)
+{
+    JS_ASSERT(*emitted == false);
+    LazyArgumentsType isArguments = oracle->propertyReadMagicArguments(script_, pc);
+
+    if (isArguments == MaybeArguments)
+        return abort("Type is not definitely lazy arguments.");
+    if (isArguments != DefinitelyArguments)
+        return true;
+    if (JSOp(*pc) != JSOP_LENGTH)
+        return true;
+
+    *emitted = true;
+    return jsop_arguments_length();
+}
+
+bool
+IonBuilder::getPropTryConstant(bool *emitted, MDefinition *obj, HandleId id,
+                               types::StackTypeSet *barrier, types::StackTypeSet *types,
+                               TypeOracle::UnaryTypes unaryTypes)
+{
+    JS_ASSERT(*emitted == false);
     JSObject *singleton = types ? types->getSingleton() : NULL;
-    if (singleton && !barrier) {
-        bool isKnownConstant, testObject;
-        RootedObject global(cx, &script_->global());
-        if (!TestSingletonPropertyTypes(cx, unaryTypes.inTypes,
-                                        global, id,
-                                        &isKnownConstant, &testObject))
+    if (!singleton || barrier)
+        return true;
+
+    RootedObject global(cx, &script_->global());
+
+    bool isConstant, testObject;
+    if (!TestSingletonPropertyTypes(cx, unaryTypes.inTypes, global, id, &isConstant, &testObject))
+        return false;
+
+    if (!isConstant)
+        return true;
+
+    // Property access is a known constant -- safe to emit.
+    if (testObject)
+        current->add(MGuardObject::New(obj));
+
+    MConstant *known = MConstant::New(ObjectValue(*singleton));
+    if (singleton->isFunction()) {
+        RootedFunction singletonFunc(cx, singleton->toFunction());
+        if (TestAreKnownDOMTypes(cx, unaryTypes.inTypes) &&
+            TestShouldDOMCall(cx, unaryTypes.inTypes, singletonFunc))
         {
-            return false;
-        }
-
-        if (isKnownConstant) {
-            if (testObject) {
-                MGuardObject *guard = MGuardObject::New(obj);
-                current->add(guard);
-            }
-            MConstant *known = MConstant::New(ObjectValue(*singleton));
-            current->add(known);
-            current->push(known);
-            if (singleton->isFunction()) {
-                RootedFunction singletonFunc(cx, singleton->toFunction());
-                if (TestAreKnownDOMTypes(cx, unaryTypes.inTypes) &&
-                    TestShouldDOMCall(cx, unaryTypes.inTypes, singletonFunc))
-                {
-                    FreezeDOMTypes(cx, unaryTypes.inTypes);
-                    known->setDOMFunction();
-                }
-            }
-            return true;
+            FreezeDOMTypes(cx, unaryTypes.inTypes);
+            known->setDOMFunction();
         }
     }
 
-    if (types::TypeSet *propTypes = GetDefiniteSlot(cx, unaryTypes.inTypes, name)) {
-        MDefinition *useObj = obj;
-        if (unaryTypes.inTypes && unaryTypes.inTypes->baseFlags()) {
-            MGuardObject *guard = MGuardObject::New(obj);
-            current->add(guard);
-            useObj = guard;
-        }
-        MLoadFixedSlot *fixed = MLoadFixedSlot::New(useObj, propTypes->definiteSlot());
-        if (!barrier)
-            fixed->setResultType(unary.rval);
-
-        current->add(fixed);
-        current->push(fixed);
-
-        return pushTypeBarrier(fixed, types, barrier);
+    current->add(known);
+    current->push(known);
+
+    *emitted = true;
+    return true;
+}
+
+bool
+IonBuilder::getPropTryDefiniteSlot(bool *emitted, MDefinition *obj, HandlePropertyName name,
+                                   types::StackTypeSet *barrier, types::StackTypeSet *types,
+                                   TypeOracle::Unary unary, TypeOracle::UnaryTypes unaryTypes)
+{
+    JS_ASSERT(*emitted == false);
+    types::TypeSet *propTypes = GetDefiniteSlot(cx, unaryTypes.inTypes, name);
+    if (!propTypes)
+        return true;
+
+    MDefinition *useObj = obj;
+    if (unaryTypes.inTypes && unaryTypes.inTypes->baseFlags()) {
+        MGuardObject *guard = MGuardObject::New(obj);
+        current->add(guard);
+        useObj = guard;
     }
 
-    // Attempt to inline common property getter. At least patch to call instead.
+    MLoadFixedSlot *fixed = MLoadFixedSlot::New(useObj, propTypes->definiteSlot());
+    if (!barrier)
+        fixed->setResultType(unary.rval);
+
+    current->add(fixed);
+    current->push(fixed);
+
+    if (!pushTypeBarrier(fixed, types, barrier))
+        return false;
+
+    *emitted = true;
+    return true;
+}
+
+bool
+IonBuilder::getPropTryCommonGetter(bool *emitted, MDefinition *obj, HandleId id,
+                                   types::StackTypeSet *barrier, types::StackTypeSet *types,
+                                   TypeOracle::UnaryTypes unaryTypes)
+{
+    JS_ASSERT(*emitted == false);
     JSFunction *commonGetter;
     bool isDOM;
+
     if (!TestCommonPropFunc(cx, unaryTypes.inTypes, id, &commonGetter, true, &isDOM))
-         return false;
-     if (commonGetter) {
-        RootedFunction getter(cx, commonGetter);
-        if (isDOM && TestShouldDOMCall(cx, unaryTypes.inTypes, getter)) {
-            const JSJitInfo *jitinfo = getter->jitInfo();
-            MGetDOMProperty *get = MGetDOMProperty::New(jitinfo->op, obj, jitinfo->isInfallible);
-
-            current->add(get);
-            current->push(get);
-
-            if (!resumeAfter(get))
-                return false;
-
-            return pushTypeBarrier(get, types, barrier);
-        }
-        // Spoof stack to expected state for call.
-        pushConstant(ObjectValue(*commonGetter));
-
-        MPassArg *wrapper = MPassArg::New(obj);
-        current->push(wrapper);
-        current->add(wrapper);
-
-        return makeCallBarrier(getter, 0, false, types, barrier);
+        return false;
+    if (!commonGetter)
+        return true;
+
+    RootedFunction getter(cx, commonGetter);
+
+    if (isDOM && TestShouldDOMCall(cx, unaryTypes.inTypes, getter)) {
+        const JSJitInfo *jitinfo = getter->jitInfo();
+        MGetDOMProperty *get = MGetDOMProperty::New(jitinfo->op, obj, jitinfo->isInfallible);
+        current->add(get);
+        current->push(get);
+
+        if (!resumeAfter(get))
+            return false;
+        if (!pushTypeBarrier(get, types, barrier))
+            return false;
+
+        *emitted = true;
+        return true;
     }
 
-    // If the input is guaranteed to be an object, then we want
-    // to specialize it via a slot load or an IC.
-
+    // Spoof stack to expected state for call.
+    pushConstant(ObjectValue(*commonGetter));
+
+    MPassArg *wrapper = MPassArg::New(obj);
+    current->add(wrapper);
+    current->push(wrapper);
+
+    if (!makeCallBarrier(getter, 0, false, types, barrier))
+        return false;
+
+    *emitted = true;
+    return true;
+}
+
+bool
+IonBuilder::getPropTryMonomorphic(bool *emitted, MDefinition *obj, HandleId id,
+                                  types::StackTypeSet *barrier, TypeOracle::Unary unary,
+                                  TypeOracle::UnaryTypes unaryTypes)
+{
+    JS_ASSERT(*emitted == false);
     bool accessGetter = oracle->propertyReadAccessGetter(script_, pc);
+
+    if (unary.ival != MIRType_Object)
+        return true;
+
+    Shape *objShape = mjit::GetPICSingleShape(cx, script_, pc, info().constructing());
+    if (!objShape || objShape->inDictionary()) {
+        spew("GETPROP not monomorphic");
+        return true;
+    }
+
+    // The JM IC was monomorphic, so we inline the property access as long as
+    // the shape is not in dictionary made. We cannot be sure that the shape is
+    // still a lastProperty, and calling Shape::search() on dictionary mode
+    // shapes that aren't lastProperty is invalid.
+    MGuardShape *guard = MGuardShape::New(obj, objShape, Bailout_CachedShapeGuard);
+    current->add(guard);
+
+    spew("Inlining monomorphic GETPROP");
+    Shape *shape = objShape->search(cx, id);
+    JS_ASSERT(shape);
+
     MIRType rvalType = unary.rval;
     if (barrier || IsNullOrUndefined(unary.rval) || accessGetter)
         rvalType = MIRType_Value;
 
-    if (unary.ival == MIRType_Object) {
-        Shape *objShape;
-        if ((objShape = mjit::GetPICSingleShape(cx, script_, pc, info().constructing())) &&
-            !objShape->inDictionary())
-        {
-            // The JM IC was monomorphic, so we inline the property access as
-            // long as the shape is not in dictionary mode. We cannot be sure
-            // that the shape is still a lastProperty, and calling
-            // Shape::search() on dictionary mode shapes that aren't
-            // lastProperty is invalid.
-            MGuardShape *guard = MGuardShape::New(obj, objShape, Bailout_CachedShapeGuard);
-            current->add(guard);
-
-            spew("Inlining monomorphic GETPROP");
-
-            Shape *shape = objShape->search(cx, NameToId(name));
-            JS_ASSERT(shape);
-
-            return loadSlot(obj, shape, rvalType);
-        }
-
-        spew("GETPROP not monomorphic");
-
-        MGetPropertyCache *load = MGetPropertyCache::New(obj, name);
-        load->setResultType(rvalType);
-
-        // Try to mark the cache as idempotent. We only do this if JM is enabled
-        // (its ICs are used to mark property reads as likely non-idempotent) or
-        // if we are compiling eagerly (to improve test coverage).
-        if ((cx->methodJitEnabled || js_IonOptions.eagerCompilation) &&
-            !invalidatedIdempotentCache())
-        {
-            if (oracle->propertyReadIdempotent(script_, pc, id))
-                load->setIdempotent();
-        }
-
-        if (JSOp(*pc) == JSOP_CALLPROP) {
-            if (!annotateGetPropertyCache(cx, obj, load, unaryTypes.inTypes, types))
-                return false;
-        }
-
-        // If the cache is known to access getters, then enable generation of
-        // getter stubs and set its result type to value.
-        if (accessGetter)
-            load->setAllowGetters();
-
-        ins = load;
-    } else if (obj->type() == MIRType_Value && unaryTypes.inTypes->objectOrSentinel()) {
-        spew("GETPROP is object-or-sentinel");
-
-        MGetPropertyCache *load = MGetPropertyCache::New(obj, name);
-        load->setResultType(rvalType);
-        if (accessGetter)
-            load->setAllowGetters();
-
-        ins = load;
-    } else {
-        ins = MCallGetProperty::New(obj, name);
+    if (!loadSlot(obj, shape, rvalType))
+        return false;
+
+    *emitted = true;
+    return true;
+}
+
+bool
+IonBuilder::getPropTryPolymorphic(bool *emitted, MDefinition *obj,
+                                  HandlePropertyName name, HandleId id,
+                                  types::StackTypeSet *barrier, types::StackTypeSet *types,
+                                  TypeOracle::Unary unary, TypeOracle::UnaryTypes unaryTypes)
+{
+    JS_ASSERT(*emitted == false);
+    bool accessGetter = oracle->propertyReadAccessGetter(script_, pc);
+
+    // The input value must either be an object, or we should have strong suspicions
+    // that it can be safely unboxed to an object.
+    if (unary.ival != MIRType_Object && !unaryTypes.inTypes->objectOrSentinel())
+        return true;
+
+    MIRType rvalType = unary.rval;
+    if (barrier || IsNullOrUndefined(unary.rval) || accessGetter)
+        rvalType = MIRType_Value;
+
+    MGetPropertyCache *load = MGetPropertyCache::New(obj, name);
+    load->setResultType(rvalType);
+
+    // Try to mark the cache as idempotent. We only do this if JM is enabled
+    // (its ICs are used to mark property reads as likely non-idempotent) or
+    // if we are compiling eagerly (to improve test coverage).
+    if (unary.ival == MIRType_Object &&
+        (cx->methodJitEnabled || js_IonOptions.eagerCompilation) &&
+        !invalidatedIdempotentCache())
+    {
+        if (oracle->propertyReadIdempotent(script_, pc, id))
+            load->setIdempotent();
     }
 
-    current->add(ins);
-    current->push(ins);
-
-    if (ins->isEffectful() && !resumeAfter(ins))
+    if (JSOp(*pc) == JSOP_CALLPROP) {
+        if (!annotateGetPropertyCache(cx, obj, load, unaryTypes.inTypes, types))
+            return false;
+    }
+
+    // If the cache is known to access getters, then enable generation of getter stubs.
+    if (accessGetter)
+        load->setAllowGetters();
+
+    current->add(load);
+    current->push(load);
+
+    if (load->isEffectful() && !resumeAfter(load))
         return false;
 
-    if (ins->isCallGetProperty() || accessGetter)
-        monitorResult(ins, barrier, types);
-    return pushTypeBarrier(ins, types, barrier);
+    if (accessGetter)
+        monitorResult(load, barrier, types);
+
+    if (!pushTypeBarrier(load, types, barrier))
+        return false;
+
+    *emitted = true;
+    return true;
 }
 
 bool
 IonBuilder::jsop_setprop(HandlePropertyName name)
 {
     MDefinition *value = current->pop();
     MDefinition *obj = current->pop();
 
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -283,16 +283,35 @@ class IonBuilder : public MIRGenerator
 
     JSObject *getNewArrayTemplateObject(uint32 count);
 
     bool invalidatedIdempotentCache();
 
     bool loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType);
     bool storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool needsBarrier);
 
+    // jsop_getprop() helpers.
+    bool getPropTryArgumentsLength(bool *emitted);
+    bool getPropTryConstant(bool *emitted, MDefinition *obj, HandleId id,
+                            types::StackTypeSet *barrier, types::StackTypeSet *types,
+                            TypeOracle::UnaryTypes unaryTypes);
+    bool getPropTryDefiniteSlot(bool *emitted, MDefinition *obj, HandlePropertyName name,
+                            types::StackTypeSet *barrier, types::StackTypeSet *types,
+                            TypeOracle::Unary unary, TypeOracle::UnaryTypes unaryTypes);
+    bool getPropTryCommonGetter(bool *emitted, MDefinition *obj, HandleId id,
+                            types::StackTypeSet *barrier, types::StackTypeSet *types,
+                            TypeOracle::UnaryTypes unaryTypes);
+    bool getPropTryMonomorphic(bool *emitted, MDefinition *obj, HandleId id,
+                            types::StackTypeSet *barrier, TypeOracle::Unary unary,
+                            TypeOracle::UnaryTypes unaryTypes);
+    bool getPropTryPolymorphic(bool *emitted, MDefinition *obj,
+                               HandlePropertyName name, HandleId id,
+                               types::StackTypeSet *barrier, types::StackTypeSet *types,
+                               TypeOracle::Unary unary, TypeOracle::UnaryTypes unaryTypes);
+
     bool jsop_add(MDefinition *left, MDefinition *right);
     bool jsop_bitnot();
     bool jsop_bitop(JSOp op);
     bool jsop_binary(JSOp op);
     bool jsop_binary(JSOp op, MDefinition *left, MDefinition *right);
     bool jsop_pos();
     bool jsop_neg();
     bool jsop_defvar(uint32 index);