Bug 894596 - Bake the values of constant global variables into Ion code, r=jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 03 Sep 2014 09:54:41 -0600
changeset 203263 686300375fd6
parent 203262 873f3069216a
child 203264 3a343c27fc7a
push id48643
push userbhackett@mozilla.com
push dateWed, 03 Sep 2014 15:54:48 +0000
treeherdermozilla-inbound@686300375fd6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs894596
milestone35.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
Bug 894596 - Bake the values of constant global variables into Ion code, r=jandem.
js/src/jit/BaselineIC.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/IonCaches.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsinferinlines.h
js/src/jsobj.cpp
js/src/vm/Interpreter.cpp
js/src/vm/ScopeObject-inl.h
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -7545,16 +7545,26 @@ TryAttachSetPropStub(JSContext *cx, Hand
             return false;
 
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
 
     if (IsCacheableSetPropWriteSlot(obj, oldShape, holder, shape)) {
+        // For some property writes, such as the initial overwrite of global
+        // properties, TI will not mark the property as having been
+        // overwritten. Don't attach a stub in this case, so that we don't
+        // execute another write to the property without TI seeing that write.
+        types::EnsureTrackPropertyTypes(cx, obj, id);
+        if (!types::PropertyHasBeenMarkedNonConstant(obj, id)) {
+            *attached = true;
+            return true;
+        }
+
         bool isFixedSlot;
         uint32_t offset;
         GetFixedOrDynamicSlotOffset(obj, shape->slot(), &isFixedSlot, &offset);
 
         JitSpew(JitSpew_BaselineIC, "  Generating SetProp(NativeObject.PROP) stub");
         ICSetProp_Native::Compiler compiler(cx, obj, isFixedSlot, offset);
         ICSetProp_Native *newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6558,21 +6558,28 @@ IonBuilder::getStaticName(JSObject *stat
     types::TemporaryTypeSet *types = bytecodeTypes(pc);
     BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), staticType,
                                                        name, types, /* updateObserved = */ true);
 
     JSObject *singleton = types->getSingleton();
 
     MIRType knownType = types->getKnownMIRType();
     if (barrier == BarrierKind::NoBarrier) {
+        // Try to inline properties holding a known constant object.
         if (singleton) {
-            // Try to inline a known constant value.
             if (testSingletonProperty(staticObject, name) == singleton)
                 return pushConstant(ObjectValue(*singleton));
         }
+
+        // Try to inline properties that have never been overwritten.
+        Value constantValue;
+        if (property.constant(constraints(), &constantValue))
+            return pushConstant(constantValue);
+
+        // Try to inline properties that can only have one value.
         if (knownType == MIRType_Undefined)
             return pushConstant(UndefinedValue());
         if (knownType == MIRType_Null)
             return pushConstant(NullValue());
     }
 
     MInstruction *obj = constant(ObjectValue(*staticObject));
 
@@ -6644,17 +6651,17 @@ IonBuilder::setStaticName(JSObject *stat
         property.nonData(constraints()) ||
         property.nonWritable(constraints()))
     {
         // The property has been reconfigured as non-configurable, non-enumerable
         // or non-writable.
         return jsop_setprop(name);
     }
 
-    if (!TypeSetIncludes(property.maybeTypes(), value->type(), value->resultTypeSet()))
+    if (!CanWriteProperty(constraints(), property, value))
         return jsop_setprop(name);
 
     current->pop();
 
     // Pop the bound object on the stack.
     MDefinition *obj = current->pop();
     JS_ASSERT(&obj->toConstant()->value().toObject() == staticObject);
 
@@ -8712,30 +8719,30 @@ IonBuilder::storeSlot(MDefinition *obj, 
 }
 
 bool
 IonBuilder::jsop_getprop(PropertyName *name)
 {
     bool emitted = false;
 
     MDefinition *obj = current->pop();
+    types::TemporaryTypeSet *types = bytecodeTypes(pc);
 
     // Try to optimize to a specific constant.
-    if (!getPropTryInferredConstant(&emitted, obj, name) || emitted)
+    if (!getPropTryInferredConstant(&emitted, obj, name, types) || emitted)
         return emitted;
 
     // Try to optimize arguments.length.
     if (!getPropTryArgumentsLength(&emitted, obj) || emitted)
         return emitted;
 
     // Try to optimize arguments.callee.
     if (!getPropTryArgumentsCallee(&emitted, obj, name) || emitted)
         return emitted;
 
-    types::TemporaryTypeSet *types = bytecodeTypes(pc);
     BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
                                                        obj, name, types);
 
     // Always use a call if we are performing analysis and
     // not actually emitting code, to simplify later analysis. Also skip deeper
     // analysis if there are no known types for this operation, as it will
     // always invalidate when executing.
     if (info().executionModeIsAnalysis() || types->empty()) {
@@ -8807,31 +8814,43 @@ IonBuilder::checkIsDefinitelyOptimizedAr
         return true;
     }
 
     *isOptimizedArgs = true;
     return true;
 }
 
 bool
-IonBuilder::getPropTryInferredConstant(bool *emitted, MDefinition *obj, PropertyName *name)
+IonBuilder::getPropTryInferredConstant(bool *emitted, MDefinition *obj, PropertyName *name,
+                                       types::TemporaryTypeSet *types)
 {
     JS_ASSERT(*emitted == false);
 
     // Need a result typeset to optimize.
     types::TemporaryTypeSet *objTypes = obj->resultTypeSet();
     if (!objTypes)
         return true;
 
-    Value constVal = UndefinedValue();
-    if (objTypes->propertyIsConstant(constraints(), NameToId(name), &constVal)) {
+    JSObject *singleton = objTypes->getSingleton();
+    if (!singleton)
+        return true;
+
+    types::TypeObjectKey *type = types::TypeObjectKey::get(singleton);
+    if (type->unknownProperties())
+        return true;
+
+    types::HeapTypeSetKey property = type->property(NameToId(name));
+
+    Value constantValue = UndefinedValue();
+    if (property.constant(constraints(), &constantValue)) {
         spew("Optimized constant property");
         obj->setImplicitlyUsedUnchecked();
-        if (!pushConstant(constVal))
+        if (!pushConstant(constantValue))
             return false;
+        types->addType(types::GetValueType(constantValue), alloc_->lifoAlloc());
         *emitted = true;
     }
 
     return true;
 }
 
 bool
 IonBuilder::getPropTryArgumentsLength(bool *emitted, MDefinition *obj)
@@ -9450,24 +9469,23 @@ IonBuilder::jsop_setprop(PropertyName *n
     types::TemporaryTypeSet *objTypes = obj->resultTypeSet();
     bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value,
                                                  /* canModify = */ true);
 
     // Try to emit stores to known binary data blocks
     if (!setPropTryTypedObject(&emitted, obj, name, value) || emitted)
         return emitted;
 
-    // Do not emit optimized stores to slots that may be constant.
-    if (objTypes && !objTypes->propertyMightBeConstant(constraints(), NameToId(name))) {
+    if (!barrier) {
         // Try to emit store from definite slots.
-        if (!setPropTryDefiniteSlot(&emitted, obj, name, value, barrier, objTypes) || emitted)
+        if (!setPropTryDefiniteSlot(&emitted, obj, name, value, objTypes) || emitted)
             return emitted;
 
         // Try to emit a monomorphic/polymorphic store based on baseline caches.
-        if (!setPropTryInlineAccess(&emitted, obj, name, value, barrier, objTypes) || emitted)
+        if (!setPropTryInlineAccess(&emitted, obj, name, value, objTypes) || emitted)
             return emitted;
     }
 
     // Emit a polymorphic cache.
     return setPropTryCache(&emitted, obj, name, value, barrier, objTypes);
 }
 
 bool
@@ -9634,23 +9652,20 @@ IonBuilder::setPropTryScalarPropOfTypedO
 
     *emitted = true;
     return true;
 }
 
 bool
 IonBuilder::setPropTryDefiniteSlot(bool *emitted, MDefinition *obj,
                                    PropertyName *name, MDefinition *value,
-                                   bool barrier, types::TemporaryTypeSet *objTypes)
+                                   types::TemporaryTypeSet *objTypes)
 {
     JS_ASSERT(*emitted == false);
 
-    if (barrier)
-        return true;
-
     uint32_t slot = getDefiniteSlot(obj->resultTypeSet(), name);
     if (slot == UINT32_MAX)
         return true;
 
     bool writeBarrier = false;
     for (size_t i = 0; i < obj->resultTypeSet()->getObjectCount(); i++) {
         types::TypeObjectKey *type = obj->resultTypeSet()->getObject(i);
         if (!type)
@@ -9683,25 +9698,21 @@ IonBuilder::setPropTryDefiniteSlot(bool 
         return false;
 
     *emitted = true;
     return true;
 }
 
 bool
 IonBuilder::setPropTryInlineAccess(bool *emitted, MDefinition *obj,
-                                   PropertyName *name,
-                                   MDefinition *value, bool barrier,
+                                   PropertyName *name, MDefinition *value,
                                    types::TemporaryTypeSet *objTypes)
 {
     JS_ASSERT(*emitted == false);
 
-    if (barrier)
-        return true;
-
     BaselineInspector::ShapeVector shapes(alloc());
     if (!inspector->maybeShapesForPropertyOp(pc, shapes))
         return false;
 
     if (shapes.empty())
         return true;
 
     if (!CanInlinePropertyOpShapes(shapes))
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -394,17 +394,18 @@ class IonBuilder : public MIRGenerator
                    MIRType slotType = MIRType_None);
     bool storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool needsBarrier,
                    MIRType slotType = MIRType_None);
 
     MDefinition *tryInnerizeWindow(MDefinition *obj);
 
     // jsop_getprop() helpers.
     bool checkIsDefinitelyOptimizedArguments(MDefinition *obj, bool *isOptimizedArgs);
-    bool getPropTryInferredConstant(bool *emitted, MDefinition *obj, PropertyName *name);
+    bool getPropTryInferredConstant(bool *emitted, MDefinition *obj, PropertyName *name,
+                                    types::TemporaryTypeSet *types);
     bool getPropTryArgumentsLength(bool *emitted, MDefinition *obj);
     bool getPropTryArgumentsCallee(bool *emitted, MDefinition *obj, PropertyName *name);
     bool getPropTryConstant(bool *emitted, MDefinition *obj, PropertyName *name,
                             types::TemporaryTypeSet *types);
     bool getPropTryDefiniteSlot(bool *emitted, MDefinition *obj, PropertyName *name,
                                 BarrierKind barrier, types::TemporaryTypeSet *types);
     bool getPropTryCommonGetter(bool *emitted, MDefinition *obj, PropertyName *name,
                                 types::TemporaryTypeSet *types);
@@ -430,19 +431,19 @@ class IonBuilder : public MIRGenerator
     // jsop_setprop() helpers.
     bool setPropTryCommonSetter(bool *emitted, MDefinition *obj,
                                 PropertyName *name, MDefinition *value);
     bool setPropTryCommonDOMSetter(bool *emitted, MDefinition *obj,
                                    MDefinition *value, JSFunction *setter,
                                    bool isDOM);
     bool setPropTryDefiniteSlot(bool *emitted, MDefinition *obj,
                                 PropertyName *name, MDefinition *value,
-                                bool barrier, types::TemporaryTypeSet *objTypes);
+                                types::TemporaryTypeSet *objTypes);
     bool setPropTryInlineAccess(bool *emitted, MDefinition *obj,
-                                PropertyName *name, MDefinition *value, bool barrier,
+                                PropertyName *name, MDefinition *value,
                                 types::TemporaryTypeSet *objTypes);
     bool setPropTryTypedObject(bool *emitted, MDefinition *obj,
                                PropertyName *name, MDefinition *value);
     bool setPropTryScalarPropOfTypedObject(bool *emitted,
                                            MDefinition *obj,
                                            int32_t fieldOffset,
                                            MDefinition *value,
                                            TypedObjectPrediction fieldTypeReprs);
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -2641,16 +2641,18 @@ CanInlineSetPropTypeCheck(JSObject *obj,
 {
     bool shouldCheck = false;
     types::TypeObject *type = obj->type();
     if (!type->unknownProperties()) {
         types::HeapTypeSet *propTypes = type->maybeGetProperty(id);
         if (!propTypes)
             return false;
         if (!propTypes->unknown()) {
+            if (obj->hasSingletonType() && !propTypes->nonConstantProperty())
+                return false;
             shouldCheck = true;
             if (val.constant()) {
                 // If the input is a constant, then don't bother if the barrier will always fail.
                 if (!propTypes->hasType(types::GetValueType(val.value())))
                     return false;
                 shouldCheck = false;
             } else {
                 TypedOrValueRegister reg = val.reg();
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -3878,17 +3878,17 @@ TryAddTypeBarrierForWrite(TempAllocator 
         if (!object)
             continue;
 
         if (object->unknownProperties())
             return false;
 
         jsid id = name ? NameToId(name) : JSID_VOID;
         types::HeapTypeSetKey property = object->property(id);
-        if (!property.maybeTypes())
+        if (!property.maybeTypes() || property.couldBeConstant(constraints))
             return false;
 
         if (TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet()))
             return false;
 
         // This freeze is not required for correctness, but ensures that we
         // will recompile if the property types change and the barrier can
         // potentially be removed.
@@ -3962,16 +3962,26 @@ AddTypeGuard(TempAllocator &alloc, MBasi
     current->add(guard);
 
     // For now, never move type object guards.
     guard->setNotMovable();
 
     return guard;
 }
 
+// Whether value can be written to property without changing type information.
+bool
+jit::CanWriteProperty(types::CompilerConstraintList *constraints,
+                      types::HeapTypeSetKey property, MDefinition *value)
+{
+    if (property.couldBeConstant(constraints))
+        return false;
+    return TypeSetIncludes(property.maybeTypes(), value->type(), value->resultTypeSet());
+}
+
 bool
 jit::PropertyWriteNeedsTypeBarrier(TempAllocator &alloc, types::CompilerConstraintList *constraints,
                                    MBasicBlock *current, MDefinition **pobj,
                                    PropertyName *name, MDefinition **pvalue, bool canModify)
 {
     // If any value being written is not reflected in the type information for
     // objects which obj could represent, a type barrier is needed when writing
     // the value. As for propertyReadNeedsTypeBarrier, this only applies for
@@ -3995,17 +4005,17 @@ jit::PropertyWriteNeedsTypeBarrier(TempA
 
         // TI doesn't track TypedArray objects and should never insert a type
         // barrier for them.
         if (IsTypedArrayClass(object->clasp()))
             continue;
 
         jsid id = name ? NameToId(name) : JSID_VOID;
         types::HeapTypeSetKey property = object->property(id);
-        if (!TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet())) {
+        if (!CanWriteProperty(constraints, property, *pvalue)) {
             // Either pobj or pvalue needs to be modified to filter out the
             // types which the value could have but are not in the property,
             // or a VM call is required. A VM call is always required if pobj
             // and pvalue cannot be modified.
             if (!canModify)
                 return true;
             success = TryAddTypeBarrierForWrite(alloc, constraints, current, types, name, pvalue);
             break;
@@ -4027,17 +4037,17 @@ jit::PropertyWriteNeedsTypeBarrier(TempA
         types::TypeObjectKey *object = types->getObject(i);
         if (!object || object->unknownProperties())
             continue;
         if (IsTypedArrayClass(object->clasp()))
             continue;
 
         jsid id = name ? NameToId(name) : JSID_VOID;
         types::HeapTypeSetKey property = object->property(id);
-        if (TypeSetIncludes(property.maybeTypes(), (*pvalue)->type(), (*pvalue)->resultTypeSet()))
+        if (CanWriteProperty(constraints, property, *pvalue))
             continue;
 
         if ((property.maybeTypes() && !property.maybeTypes()->empty()) || excluded)
             return true;
         excluded = object;
     }
 
     JS_ASSERT(excluded);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -11737,16 +11737,18 @@ BarrierKind PropertyReadNeedsTypeBarrier
                                          types::TemporaryTypeSet *observed);
 BarrierKind PropertyReadOnPrototypeNeedsTypeBarrier(types::CompilerConstraintList *constraints,
                                                     MDefinition *obj, PropertyName *name,
                                                     types::TemporaryTypeSet *observed);
 bool PropertyReadIsIdempotent(types::CompilerConstraintList *constraints,
                               MDefinition *obj, PropertyName *name);
 void AddObjectsForPropertyRead(MDefinition *obj, PropertyName *name,
                                types::TemporaryTypeSet *observed);
+bool CanWriteProperty(types::CompilerConstraintList *constraints,
+                      types::HeapTypeSetKey property, MDefinition *value);
 bool PropertyWriteNeedsTypeBarrier(TempAllocator &alloc, types::CompilerConstraintList *constraints,
                                    MBasicBlock *current, MDefinition **pobj,
                                    PropertyName *name, MDefinition **pvalue,
                                    bool canModify);
 
 } // namespace jit
 } // namespace js
 
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1778,16 +1778,60 @@ HeapTypeSetKey::constant(CompilerConstra
     *valOut = val;
 
     LifoAlloc *alloc = constraints->alloc();
     typedef CompilerConstraintInstance<ConstraintDataConstantProperty> T;
     constraints->add(alloc->new_<T>(alloc, *this, ConstraintDataConstantProperty()));
     return true;
 }
 
+// A constraint that never triggers recompilation.
+class ConstraintDataInert
+{
+  public:
+    explicit ConstraintDataInert() {}
+
+    const char *kind() { return "inert"; }
+
+    bool invalidateOnNewType(Type type) { return false; }
+    bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
+    bool invalidateOnNewObjectState(TypeObject *object) { return false; }
+
+    bool constraintHolds(JSContext *cx,
+                         const HeapTypeSetKey &property, TemporaryTypeSet *expected)
+    {
+        return true;
+    }
+
+    bool shouldSweep() { return false; }
+};
+
+bool
+HeapTypeSetKey::couldBeConstant(CompilerConstraintList *constraints)
+{
+    // Only singleton object properties can be marked as constants.
+    if (!object()->singleton())
+        return false;
+
+    if (!maybeTypes() || !maybeTypes()->nonConstantProperty())
+        return true;
+
+    // It is possible for a property that was not marked as constant to
+    // 'become' one, if we throw away the type property during a GC and
+    // regenerate it with the constant flag set. TypeObject::sweep only removes
+    // type properties if they have no constraints attached to them, so add
+    // inert constraints to pin these properties in place.
+
+    LifoAlloc *alloc = constraints->alloc();
+    typedef CompilerConstraintInstance<ConstraintDataInert> T;
+    constraints->add(alloc->new_<T>(alloc, *this, ConstraintDataInert()));
+
+    return false;
+}
+
 bool
 TemporaryTypeSet::filtersType(const TemporaryTypeSet *other, Type filteredType) const
 {
     if (other->unknown())
         return unknown();
 
     for (TypeFlags flag = 1; flag < TYPE_FLAG_ANYOBJECT; flag <<= 1) {
         Type type = Type::PrimitiveType(TypeFlagPrimitive(flag));
@@ -1805,105 +1849,16 @@ TemporaryTypeSet::filtersType(const Temp
             if (type != filteredType && !hasType(type))
                 return false;
         }
     }
 
     return true;
 }
 
-namespace {
-
-// A constraint that never triggers recompilation.
-class ConstraintDataInert
-{
-  public:
-    explicit ConstraintDataInert() {}
-
-    const char *kind() { return "inert"; }
-
-    bool invalidateOnNewType(Type type) { return false; }
-    bool invalidateOnNewPropertyState(TypeSet *property) { return false; }
-    bool invalidateOnNewObjectState(TypeObject *object) { return false; }
-
-    bool constraintHolds(JSContext *cx,
-                         const HeapTypeSetKey &property, TemporaryTypeSet *expected)
-    {
-        return true;
-    }
-
-    bool shouldSweep() { return false; }
-};
-
-} /* anonymous namespace */
-
-bool
-TemporaryTypeSet::propertyMightBeConstant(CompilerConstraintList *constraints, jsid id)
-{
-    if (unknownObject())
-        return true;
-
-    for (size_t i = 0; i < getObjectCount(); i++) {
-        TypeObjectKey *type = getObject(i);
-
-        // Type sets are only marked as constants when they are lazily
-        // constructed from the properties of singleton typed objects. So watch
-        // for the cases when a property either already is or might be marked
-        // as constant in the future.
-
-        if (!type || !type->isSingleObject())
-            continue;
-
-        if (type->unknownProperties())
-            return true;
-
-        HeapTypeSetKey property = type->property(id);
-        if (!property.maybeTypes() || !property.maybeTypes()->nonConstantProperty())
-            return true;
-    }
-
-    // It is possible for a property that was not marked as constant to
-    // 'become' one, if we throw away the type property during a GC and
-    // regenerate it with the constant flag set. TypeObject::sweep only removes
-    // type properties if they have no constraints attached to them, so add
-    // inert constraints to pin these properties in place.
-
-    LifoAlloc *alloc = constraints->alloc();
-    for (size_t i = 0; i < getObjectCount(); i++) {
-        TypeObjectKey *type = getObject(i);
-
-        if (!type || !type->isSingleObject())
-            continue;
-
-        HeapTypeSetKey property = type->property(id);
-
-        typedef CompilerConstraintInstance<ConstraintDataInert> T;
-        constraints->add(alloc->new_<T>(alloc, property, ConstraintDataInert()));
-    }
-
-    return false;
-}
-
-bool
-TemporaryTypeSet::propertyIsConstant(CompilerConstraintList *constraints, jsid id, Value *valOut)
-{
-    JS_ASSERT(valOut);
-
-    JSObject *singleton = getSingleton();
-    if (!singleton)
-        return false;
-
-    TypeObjectKey *type = TypeObjectKey::get(singleton);
-    if (type->unknownProperties())
-        return false;
-
-    HeapTypeSetKey property = type->property(id);
-    return property.constant(constraints, valOut);
-}
-
 TemporaryTypeSet::DoubleConversion
 TemporaryTypeSet::convertDoubleElements(CompilerConstraintList *constraints)
 {
     if (unknownObject() || !getObjectCount())
         return AmbiguousDoubleConversion;
 
     bool alwaysConvert = true;
     bool maybeConvert = false;
@@ -3027,17 +2982,17 @@ InlineAddTypeProperty(ExclusiveContext *
 
     AutoEnterAnalysis enter(cx);
 
     HeapTypeSet *types = obj->getProperty(cx, id);
     if (!types)
         return;
 
     // Clear any constant flag if it exists.
-    if (!types->nonConstantProperty()) {
+    if (!types->empty() && !types->nonConstantProperty()) {
         InferSpew(ISpewOps, "constantMutated: %sT%p%s %s",
                   InferSpewColor(types), types, InferSpewColorReset(), TypeString(type));
         types->setNonConstantProperty(cx);
     }
 
     if (types->hasType(type))
         return;
 
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -770,20 +770,16 @@ class TemporaryTypeSet : public TypeSet
     bool maybeEmulatesUndefined();
 
     /* Get the single value which can appear in this type set, otherwise nullptr. */
     JSObject *getSingleton();
 
     /* Whether any objects in the type set needs a barrier on id. */
     bool propertyNeedsBarrier(CompilerConstraintList *constraints, jsid id);
 
-    /* Whether any objects in the type set might treat id as a constant property. */
-    bool propertyMightBeConstant(CompilerConstraintList *constraints, jsid id);
-    bool propertyIsConstant(CompilerConstraintList *constraints, jsid id, Value *valOut);
-
     /*
      * Whether this set contains all types in other, except (possibly) the
      * specified type.
      */
     bool filtersType(const TemporaryTypeSet *other, Type type) const;
 
     enum DoubleConversion {
         /* All types in the set should use eager double conversion. */
@@ -1506,16 +1502,17 @@ class HeapTypeSetKey
     jit::MIRType knownMIRType(CompilerConstraintList *constraints);
     bool nonData(CompilerConstraintList *constraints);
     bool nonWritable(CompilerConstraintList *constraints);
     bool isOwnProperty(CompilerConstraintList *constraints);
     bool knownSubset(CompilerConstraintList *constraints, const HeapTypeSetKey &other);
     JSObject *singleton(CompilerConstraintList *constraints);
     bool needsBarrier(CompilerConstraintList *constraints);
     bool constant(CompilerConstraintList *constraints, Value *valOut);
+    bool couldBeConstant(CompilerConstraintList *constraints);
 };
 
 /*
  * Information about the result of the compilation of a script.  This structure
  * stored in the TypeCompartment is indexed by the RecompileInfo. This
  * indirection enables the invalidation of all constraints related to the same
  * compilation.
  */
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -443,16 +443,30 @@ CanHaveEmptyPropertyTypesForOwnProperty(
 {
     // Per the comment on TypeSet::propertySet, property type sets for global
     // objects may be empty for 'own' properties if the global property still
     // has its initial undefined value.
     return obj->is<GlobalObject>();
 }
 
 inline bool
+PropertyHasBeenMarkedNonConstant(JSObject *obj, jsid id)
+{
+    // Non-constant properties are only relevant for singleton objects.
+    if (!obj->hasSingletonType())
+        return true;
+
+    // EnsureTrackPropertyTypes must have been called on this object.
+    if (obj->type()->unknownProperties())
+        return true;
+    HeapTypeSet *types = obj->type()->maybeGetProperty(IdToTypeId(id));
+    return types->nonConstantProperty();
+}
+
+inline bool
 HasTypePropertyId(JSObject *obj, jsid id, Type type)
 {
     if (obj->hasLazyType())
         return true;
 
     if (obj->type()->unknownProperties())
         return true;
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4914,17 +4914,21 @@ js::NativeSet(typename ExecutionModeTrai
 
     if (shape->hasSlot()) {
         /* If shape has a stub setter, just store vp. */
         if (shape->hasDefaultSetter()) {
             if (mode == ParallelExecution) {
                 if (!obj->nativeSetSlotIfHasType(shape, vp))
                     return false;
             } else {
-                obj->nativeSetSlotWithType(cxArg->asExclusiveContext(), shape, vp);
+                // Global properties declared with 'var' will be initially
+                // defined with an undefined value, so don't treat the initial
+                // assignments to such properties as overwrites.
+                bool overwriting = !obj->is<GlobalObject>() || !obj->nativeGetSlot(shape->slot()).isUndefined();
+                obj->nativeSetSlotWithType(cxArg->asExclusiveContext(), shape, vp, overwriting);
             }
 
             return true;
         }
     }
 
     if (mode == ParallelExecution)
         return false;
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -2874,17 +2874,17 @@ END_CASE(JSOP_GETALIASEDVAR)
 
 CASE(JSOP_SETALIASEDVAR)
 {
     ScopeCoordinate sc = ScopeCoordinate(REGS.pc);
     ScopeObject &obj = REGS.fp()->aliasedVarScope(sc);
 
     // Avoid computing the name if no type updates are needed, as this may be
     // expensive on scopes with large numbers of variables.
-    PropertyName *name = (obj.hasSingletonType() && !obj.hasLazyType())
+    PropertyName *name = obj.hasSingletonType()
                          ? ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, REGS.pc)
                          : nullptr;
 
     obj.setAliasedVar(cx, sc, name, REGS.sp[-1]);
 }
 END_CASE(JSOP_SETALIASEDVAR)
 
 CASE(JSOP_GETARG)
--- a/js/src/vm/ScopeObject-inl.h
+++ b/js/src/vm/ScopeObject-inl.h
@@ -14,23 +14,31 @@
 namespace js {
 
 inline void
 ScopeObject::setAliasedVar(JSContext *cx, ScopeCoordinate sc, PropertyName *name, const Value &v)
 {
     JS_ASSERT(is<CallObject>() || is<ClonedBlockObject>());
     JS_STATIC_ASSERT(CallObject::RESERVED_SLOTS == BlockObject::RESERVED_SLOTS);
 
-    setSlot(sc.slot(), v);
+    // name may be null if we don't need to track side effects on the object.
+    JS_ASSERT_IF(hasSingletonType(), name);
 
-    // name may be null if we don't need to track side effects on the object.
-    if (hasSingletonType() && !hasLazyType()) {
+    if (hasSingletonType()) {
         JS_ASSERT(name);
         types::AddTypePropertyId(cx, this, NameToId(name), v);
+
+        // Keep track of properties which have ever been overwritten.
+        if (!getSlot(sc.slot()).isUndefined()) {
+            Shape *shape = nativeLookup(cx, name);
+            shape->setOverwritten();
+        }
     }
+
+    setSlot(sc.slot(), v);
 }
 
 inline void
 CallObject::setAliasedVar(JSContext *cx, AliasedFormalIter fi, PropertyName *name, const Value &v)
 {
     JS_ASSERT(name == fi->name());
     setSlot(fi.scopeSlot(), v);
     if (hasSingletonType())