Bug 1190272 - Improve type checks when storing values into unboxed objects in Ion code. r=jandem, a=abillings
authorBrian Hackett <bhackett1024@gmail.com>
Sun, 09 Aug 2015 18:55:49 -0600
changeset 281857 cdf5ffa499211fa53e163654f4b72b380753d024
parent 281856 d6c73ce94b100811d021d2e097023c46e6ace1d9
child 281858 7a647fc4d0ba97572d72e308bb016d0b9d2ab5ff
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem, abillings
bugs1190272
milestone41.0a2
Bug 1190272 - Improve type checks when storing values into unboxed objects in Ion code. r=jandem, a=abillings
js/src/jit/IonCaches.cpp
js/src/jit/MCallOptimize.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/vm/UnboxedObject.h
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -3096,22 +3096,17 @@ GenerateSetUnboxed(JSContext* cx, MacroA
         if (unboxedType == JSVAL_TYPE_OBJECT)
             masm.callPreBarrier(address, MIRType_Object);
         else if (unboxedType == JSVAL_TYPE_STRING)
             masm.callPreBarrier(address, MIRType_String);
         else
             MOZ_ASSERT(!UnboxedTypeNeedsPreBarrier(unboxedType));
     }
 
-    // If unboxed objects in this group have have never been converted to
-    // native objects then the type set check performed above ensures the value
-    // being written can be stored in the unboxed object.
-    Label* storeFailure = obj->group()->unboxedLayout().nativeGroup() ? &failure : nullptr;
-
-    masm.storeUnboxedProperty(address, unboxedType, value, storeFailure);
+    masm.storeUnboxedProperty(address, unboxedType, value, &failure);
 
     attacher.jumpRejoin(masm);
 
     masm.bind(&failurePopObject);
     masm.pop(object);
     masm.bind(&failure);
 
     attacher.jumpNextStub(masm);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -921,16 +921,23 @@ IonBuilder::inlineArrayConcat(CallInfo& 
             continue;
 
         if (key->unknownProperties())
             return InliningStatus_NotInlined;
 
         HeapTypeSetKey elemTypes = key->property(JSID_VOID);
         if (!elemTypes.knownSubset(constraints(), thisElemTypes))
             return InliningStatus_NotInlined;
+
+        if (thisGroup->clasp() == &UnboxedArrayObject::class_ &&
+            !CanStoreUnboxedType(alloc(), thisGroup->unboxedLayout().elementType(),
+                                 MIRType_Value, elemTypes.maybeTypes()))
+        {
+            return InliningStatus_NotInlined;
+        }
     }
 
     // Inline the call.
     JSObject* templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat);
     if (!templateObj || templateObj->group() != thisGroup)
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1833,16 +1833,50 @@ jit::EqualTypes(MIRType type1, Temporary
         return TypeSetIncludes(typeset1, type2, nullptr);
     if (!typeset1 && typeset2)
         return TypeSetIncludes(typeset2, type1, nullptr);
 
     // Typesets should equal.
     return typeset1->equals(typeset2);
 }
 
+// Tests whether input/inputTypes can always be stored to an unboxed
+// object/array property with the given unboxed type.
+bool
+jit::CanStoreUnboxedType(TempAllocator& alloc,
+                         JSValueType unboxedType, MIRType input, TypeSet* inputTypes)
+{
+    TemporaryTypeSet types;
+
+    switch (unboxedType) {
+      case JSVAL_TYPE_BOOLEAN:
+      case JSVAL_TYPE_INT32:
+      case JSVAL_TYPE_DOUBLE:
+      case JSVAL_TYPE_STRING:
+        types.addType(TypeSet::PrimitiveType(unboxedType), alloc.lifoAlloc());
+        break;
+
+      case JSVAL_TYPE_OBJECT:
+        types.addType(TypeSet::AnyObjectType(), alloc.lifoAlloc());
+        types.addType(TypeSet::NullType(), alloc.lifoAlloc());
+        break;
+
+      default:
+        MOZ_CRASH("Bad unboxed type");
+    }
+
+    return TypeSetIncludes(&types, input, inputTypes);
+}
+
+static bool
+CanStoreUnboxedType(TempAllocator& alloc, JSValueType unboxedType, MDefinition* value)
+{
+    return CanStoreUnboxedType(alloc, unboxedType, value->type(), value->resultTypeSet());
+}
+
 bool
 MPhi::specializeType()
 {
 #ifdef DEBUG
     MOZ_ASSERT(!specialized_);
     specialized_ = true;
 #endif
 
@@ -5349,16 +5383,30 @@ jit::PropertyWriteNeedsTypeBarrier(TempA
             // 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,
                                                 implicitType);
             break;
         }
+
+        // Perform additional filtering to make sure that any unboxed property
+        // being written can accommodate the value.
+        if (key->isGroup() && key->group()->maybeUnboxedLayout()) {
+            const UnboxedLayout& layout = key->group()->unboxedLayout();
+            if (name) {
+                const UnboxedLayout::Property* property = layout.lookup(name);
+                if (property && !CanStoreUnboxedType(alloc, property->type, *pvalue))
+                    return true;
+            } else {
+                if (layout.isArray() && !CanStoreUnboxedType(alloc, layout.elementType(), *pvalue))
+                    return true;
+            }
+        }
     }
 
     if (success)
         return false;
 
     // If all of the objects except one have property types which reflect the
     // value, and the remaining object has no types at all for the property,
     // add a guard that the object does not have that remaining object's type.
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -2836,16 +2836,20 @@ MergeTypes(MIRType* ptype, TemporaryType
 
 bool
 TypeSetIncludes(TypeSet* types, MIRType input, TypeSet* inputTypes);
 
 bool
 EqualTypes(MIRType type1, TemporaryTypeSet* typeset1,
            MIRType type2, TemporaryTypeSet* typeset2);
 
+bool
+CanStoreUnboxedType(TempAllocator& alloc,
+                    JSValueType unboxedType, MIRType input, TypeSet* inputTypes);
+
 #ifdef DEBUG
 bool
 IonCompilationCanUseNurseryPointers();
 #endif
 
 // Helper class to check that GC pointers embedded in MIR instructions are in
 // in the nursery only when the store buffer has been marked as needing to
 // cancel all ion compilations. Otherwise, off-thread Ion compilation and
--- a/js/src/vm/UnboxedObject.h
+++ b/js/src/vm/UnboxedObject.h
@@ -124,17 +124,17 @@ class UnboxedLayout : public mozilla::Li
         js_free(traceList_);
 
         nativeGroup_.init(nullptr);
         nativeShape_.init(nullptr);
         replacementGroup_.init(nullptr);
         constructorCode_.init(nullptr);
     }
 
-    bool isArray() {
+    bool isArray() const {
         return elementType_ != JSVAL_TYPE_MAGIC;
     }
 
     void detachFromCompartment();
 
     const PropertyVector& properties() const {
         return properties_;
     }