Bug 824393 - Part 0: Open SetPropertyIC to cases with uncertain TI. (r=bhackett)
authorEric Faust <efaustbmo@gmail.com>
Fri, 30 Aug 2013 18:50:36 -0700
changeset 145149 c1ccfd8f31bf1310797c02c0babca78d4c3c0e27
parent 145148 6f82c0e76f2d2039a5c85b1f51f8efec767d0f32
child 145150 7180f4ffb7f18bd2580998384dee03c0b880ea0e
push id33169
push userefaustbmo@gmail.com
push dateSat, 31 Aug 2013 01:52:05 +0000
treeherdermozilla-inbound@3cd1d0ccb812 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs824393
milestone26.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 824393 - Part 0: Open SetPropertyIC to cases with uncertain TI. (r=bhackett)
js/src/jit/CodeGenerator.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonCaches.cpp
js/src/jit/IonCaches.h
js/src/jit/IonMacroAssembler.cpp
js/src/jit/IonMacroAssembler.h
js/src/jit/MIR.h
js/src/jit/RegisterSets.h
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -6182,33 +6182,35 @@ CodeGenerator::visitCallDeleteProperty(L
 
 bool
 CodeGenerator::visitSetPropertyCacheV(LSetPropertyCacheV *ins)
 {
     RegisterSet liveRegs = ins->safepoint()->liveRegs();
     Register objReg = ToRegister(ins->getOperand(0));
     ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetPropertyCacheV::Value));
 
-    SetPropertyIC cache(liveRegs, objReg, ins->mir()->name(), value, ins->mir()->strict());
+    SetPropertyIC cache(liveRegs, objReg, ins->mir()->name(), value, ins->mir()->strict(),
+                        ins->mir()->needsTypeBarrier());
     return addCache(ins, allocateCache(cache));
 }
 
 bool
 CodeGenerator::visitSetPropertyCacheT(LSetPropertyCacheT *ins)
 {
     RegisterSet liveRegs = ins->safepoint()->liveRegs();
     Register objReg = ToRegister(ins->getOperand(0));
     ConstantOrRegister value;
 
     if (ins->getOperand(1)->isConstant())
         value = ConstantOrRegister(*ins->getOperand(1)->toConstant());
     else
         value = TypedOrValueRegister(ins->valueType(), ToAnyRegister(ins->getOperand(1)));
 
-    SetPropertyIC cache(liveRegs, objReg, ins->mir()->name(), value, ins->mir()->strict());
+    SetPropertyIC cache(liveRegs, objReg, ins->mir()->name(), value, ins->mir()->strict(),
+                        ins->mir()->needsTypeBarrier());
     return addCache(ins, allocateCache(cache));
 }
 
 typedef bool (*SetPropertyICFn)(JSContext *, size_t, HandleObject, HandleValue);
 const VMFunction SetPropertyIC::UpdateInfo =
     FunctionInfo<SetPropertyICFn>(SetPropertyIC::update);
 
 bool
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -8607,21 +8607,18 @@ IonBuilder::setPropTryInlineAccess(bool 
 
 bool
 IonBuilder::setPropTryCache(bool *emitted, MDefinition *obj,
                             HandlePropertyName name, MDefinition *value,
                             bool barrier, types::StackTypeSet *objTypes)
 {
     JS_ASSERT(*emitted == false);
 
-    if (barrier)
-        return true;
-
     // Emit SetPropertyCache.
-    MSetPropertyCache *ins = MSetPropertyCache::New(obj, value, name, script()->strict);
+    MSetPropertyCache *ins = MSetPropertyCache::New(obj, value, name, script()->strict, barrier);
 
     RootedId id(cx, NameToId(name));
     if (!objTypes || objTypes->propertyNeedsBarrier(cx, id))
         ins->setNeedsBarrier();
 
     current->add(ins);
     current->push(value);
 
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -1940,19 +1940,58 @@ IonCache::destroy()
 
 bool
 SetPropertyIC::attachNativeExisting(JSContext *cx, IonScript *ion,
                                     HandleObject obj, HandleShape shape)
 {
     MacroAssembler masm(cx);
     RepatchStubAppender attacher(*this);
 
-    attacher.branchNextStub(masm, Assembler::NotEqual,
-                            Address(object(), JSObject::offsetOfShape()),
-                            ImmGCPtr(obj->lastProperty()));
+    Label failures;
+    masm.branchPtr(Assembler::NotEqual,
+                   Address(object(), JSObject::offsetOfShape()),
+                   ImmGCPtr(obj->lastProperty()), &failures);
+
+    // Guard that the incoming value is in the type set for the property
+    // if a type barrier is required.
+    if (needsTypeBarrier() && !value().constant()) {
+        // We can't do anything that would change the HeapTypeSet, so
+        // just guard that it's already there.
+
+        // Obtain and guard on the TypeObject of the object.
+        types::TypeObject *type = obj->getType(cx);
+        masm.branchPtr(Assembler::NotEqual,
+                       Address(object(), JSObject::offsetOfType()),
+                       ImmGCPtr(type), &failures);
+
+        if (!type->unknownProperties()) {
+            TypedOrValueRegister valReg = value().reg();
+            RootedId id(cx, types::IdToTypeId(AtomToId(name())));
+            types::HeapTypeSet *propTypes = type->maybeGetProperty(cx, id);
+            JS_ASSERT(propTypes);
+
+            if (!propTypes->unknown()) {
+                Label barrierSuccess;
+                Label barrierFailure;
+
+                Register scratchReg = object();
+                masm.push(scratchReg);
+
+                masm.guardTypeSet(valReg, propTypes, scratchReg,
+                                  &barrierSuccess, &barrierFailure);
+
+                masm.bind(&barrierFailure);
+                masm.pop(object());
+                masm.jump(&failures);
+
+                masm.bind(&barrierSuccess);
+                masm.pop(object());
+            }
+        }
+    }
 
     if (obj->isFixedSlot(shape->slot())) {
         Address addr(object(), JSObject::getFixedSlotOffset(shape->slot()));
 
         if (cx->zone()->needsBarrier())
             masm.callPreBarrier(addr, MIRType_Value);
 
         masm.storeConstantOrRegister(value(), addr);
@@ -1965,16 +2004,19 @@ SetPropertyIC::attachNativeExisting(JSCo
         if (cx->zone()->needsBarrier())
             masm.callPreBarrier(addr, MIRType_Value);
 
         masm.storeConstantOrRegister(value(), addr);
     }
 
     attacher.jumpRejoin(masm);
 
+    masm.bind(&failures);
+    attacher.jumpNextStub(masm);
+
     return linkAndAttachStub(cx, masm, attacher, ion, "setting");
 }
 
 static bool
 IsCacheableSetPropCallNative(HandleObject obj, HandleObject holder, HandleShape shape)
 {
     if (!shape || !IsCacheableProtoChain(obj, holder))
         return false;
@@ -2272,32 +2314,46 @@ IsPropertyInlineable(JSObject *obj)
 
     if (obj->watched())
         return false;
 
     return true;
 }
 
 static bool
-IsPropertySetInlineable(JSContext *cx, HandleObject obj, HandleId id, MutableHandleShape pshape)
+IsPropertySetInlineable(JSContext *cx, const SetPropertyIC &cache, HandleObject obj,
+                        HandleId id, MutableHandleShape pshape)
 {
     Shape *shape = obj->nativeLookup(cx, id);
 
     if (!shape)
         return false;
 
     if (!shape->hasSlot())
         return false;
 
     if (!shape->hasDefaultSetter())
         return false;
 
     if (!shape->writable())
         return false;
 
+    types::TypeObject *type = obj->getType(cx);
+    if (cache.needsTypeBarrier() && !type->unknownProperties()) {
+        RootedId typeId(cx, types::IdToTypeId(id));
+        types::HeapTypeSet *propTypes = type->maybeGetProperty(cx, typeId);
+        if (!propTypes)
+            return false;
+        if (cache.value().constant() && !propTypes->unknown()) {
+            // If the input is a constant, then don't bother if the barrier will always fail.
+            if (!propTypes->hasType(types::GetValueType(cache.value().value())))
+                return false;
+        }
+    }
+
     pshape.set(shape);
 
     return true;
 }
 
 static bool
 IsPropertyAddInlineable(JSContext *cx, HandleObject obj, HandleId id, uint32_t oldSlots,
                         MutableHandleShape pShape)
@@ -2367,17 +2423,17 @@ SetPropertyIC::update(JSContext *cx, siz
     RootedObject holder(cx);
 
     // Stop generating new stubs once we hit the stub count limit, see
     // GetPropertyCache.
     bool inlinable = cache.canAttachStub() && IsPropertyInlineable(obj);
     bool addedSetterStub = false;
     if (inlinable) {
         RootedShape shape(cx);
-        if (IsPropertySetInlineable(cx, obj, id, &shape)) {
+        if (IsPropertySetInlineable(cx, cache, obj, id, &shape)) {
             if (!cache.attachNativeExisting(cx, ion, obj, shape))
                 return false;
             addedSetterStub = true;
         } else {
             RootedObject holder(cx);
             if (!JSObject::lookupProperty(cx, obj, name, &holder, &shape))
                 return false;
 
@@ -2394,17 +2450,18 @@ SetPropertyIC::update(JSContext *cx, siz
     uint32_t oldSlots = obj->numDynamicSlots();
     RootedShape oldShape(cx, obj->lastProperty());
 
     // Set/Add the property on the object, the inlined cache are setup for the next execution.
     if (!SetProperty(cx, obj, name, value, cache.strict(), cache.pc()))
         return false;
 
     // The property did not exist before, now we can try to inline the property add.
-    if (inlinable && !addedSetterStub && obj->lastProperty() != oldShape &&
+    if (inlinable && !addedSetterStub && !cache.needsTypeBarrier() &&
+        obj->lastProperty() != oldShape &&
         IsPropertyAddInlineable(cx, obj, id, oldSlots, &shape))
     {
         RootedShape newShape(cx, obj->lastProperty());
         if (!cache.attachNativeAdding(cx, ion, obj, oldShape, newShape, shape))
             return false;
     }
 
     return true;
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -645,25 +645,27 @@ class SetPropertyIC : public RepatchIonC
     // Registers live after the cache, excluding output registers. The initial
     // value of these registers must be preserved by the cache.
     RegisterSet liveRegs_;
 
     Register object_;
     PropertyName *name_;
     ConstantOrRegister value_;
     bool strict_;
+    bool needsTypeBarrier_;
 
   public:
     SetPropertyIC(RegisterSet liveRegs, Register object, PropertyName *name,
-                  ConstantOrRegister value, bool strict)
+                  ConstantOrRegister value, bool strict, bool needsTypeBarrier)
       : liveRegs_(liveRegs),
         object_(object),
         name_(name),
         value_(value),
-        strict_(strict)
+        strict_(strict),
+        needsTypeBarrier_(needsTypeBarrier)
     {
     }
 
     CACHE_HEADER(SetProperty)
 
     Register object() const {
         return object_;
     }
@@ -671,16 +673,19 @@ class SetPropertyIC : public RepatchIonC
         return name_;
     }
     ConstantOrRegister value() const {
         return value_;
     }
     bool strict() const {
         return strict_;
     }
+    bool needsTypeBarrier() const {
+        return needsTypeBarrier_;
+    }
 
     bool attachNativeExisting(JSContext *cx, IonScript *ion, HandleObject obj, HandleShape shape);
     bool attachSetterCall(JSContext *cx, IonScript *ion, HandleObject obj,
                           HandleObject holder, HandleShape shape, void *returnAddr);
     bool attachNativeAdding(JSContext *cx, IonScript *ion, JSObject *obj, HandleShape oldshape,
                             HandleShape newshape, HandleShape propshape);
 
     static bool
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -120,16 +120,23 @@ MacroAssembler::guardType(const Source &
     guardTypeSet(address, &wrapper, scratch, matched, miss);
 }
 
 template void MacroAssembler::guardTypeSet(const Address &address, const types::StackTypeSet *types,
                                            Register scratch, Label *matched, Label *miss);
 template void MacroAssembler::guardTypeSet(const ValueOperand &value, const types::StackTypeSet *types,
                                            Register scratch, Label *matched, Label *miss);
 
+template void MacroAssembler::guardTypeSet(const Address &address, const types::HeapTypeSet *types,
+                                           Register scratch, Label *matched, Label *miss);
+template void MacroAssembler::guardTypeSet(const ValueOperand &value, const types::HeapTypeSet *types,
+                                           Register scratch, Label *matched, Label *miss);
+template void MacroAssembler::guardTypeSet(const TypedOrValueRegister &reg, const types::HeapTypeSet *types,
+                                           Register scratch, Label *matched, Label *miss);
+
 template void MacroAssembler::guardTypeSet(const Address &address, const types::TypeSet *types,
                                            Register scratch, Label *matched, Label *miss);
 template void MacroAssembler::guardTypeSet(const ValueOperand &value, const types::TypeSet *types,
                                            Register scratch, Label *matched, Label *miss);
 
 template void MacroAssembler::guardTypeSet(const Address &address, const TypeWrapper *types,
                                            Register scratch, Label *matched, Label *miss);
 template void MacroAssembler::guardTypeSet(const ValueOperand &value, const TypeWrapper *types,
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -600,16 +600,32 @@ class MacroAssembler : public MacroAssem
 
     Register extractString(const Address &address, Register scratch) {
         return extractObject(address, scratch);
     }
     Register extractString(const ValueOperand &value, Register scratch) {
         return extractObject(value, scratch);
     }
 
+    using MacroAssemblerSpecific::extractTag;
+    Register extractTag(const TypedOrValueRegister &reg, Register scratch) {
+        if (reg.hasValue()) {
+            return extractTag(reg.valueReg(), scratch);
+        }
+        mov(ImmWord(ValueTypeFromMIRType(reg.type())), scratch);
+        return scratch;
+    }
+
+    using MacroAssemblerSpecific::extractObject;
+    Register extractObject(const TypedOrValueRegister &reg, Register scratch) {
+        if (reg.hasValue())
+            return extractObject(reg.valueReg(), scratch);
+        return reg.typedReg().gpr();
+    }
+
     // Inline version of js_TypedArray_uint8_clamp_double.
     // This function clobbers the input register.
     void clampDoubleToUint8(FloatRegister input, Register output);
 
     using MacroAssemblerSpecific::ensureDouble;
 
     void ensureDouble(const Address &source, FloatRegister dest, Label *failure) {
         Label isDouble, done;
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -6669,31 +6669,40 @@ class MCallSetProperty
         return true;
     }
 };
 
 class MSetPropertyCache
   : public MSetPropertyInstruction,
     public SingleObjectPolicy
 {
-    MSetPropertyCache(MDefinition *obj, MDefinition *value, HandlePropertyName name, bool strict)
-      : MSetPropertyInstruction(obj, value, name, strict)
+    bool needsTypeBarrier_;
+
+    MSetPropertyCache(MDefinition *obj, MDefinition *value, HandlePropertyName name, bool strict,
+                      bool typeBarrier)
+      : MSetPropertyInstruction(obj, value, name, strict),
+        needsTypeBarrier_(typeBarrier)
     {
     }
 
   public:
     INSTRUCTION_HEADER(SetPropertyCache)
 
-    static MSetPropertyCache *New(MDefinition *obj, MDefinition *value, HandlePropertyName name, bool strict) {
-        return new MSetPropertyCache(obj, value, name, strict);
+    static MSetPropertyCache *New(MDefinition *obj, MDefinition *value, HandlePropertyName name,
+                                  bool strict, bool typeBarrier) {
+        return new MSetPropertyCache(obj, value, name, strict, typeBarrier);
     }
 
     TypePolicy *typePolicy() {
         return this;
     }
+
+    bool needsTypeBarrier() const {
+        return needsTypeBarrier_;
+    }
 };
 
 class MSetElementCache
   : public MSetElementInstruction,
     public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >
 {
     bool strict_;
 
--- a/js/src/jit/RegisterSets.h
+++ b/js/src/jit/RegisterSets.h
@@ -158,16 +158,25 @@ class TypedOrValueRegister
         JS_ASSERT(hasTyped());
         return *data.typed.addr();
     }
     ValueOperand &dataValue() {
         JS_ASSERT(hasValue());
         return *data.value.addr();
     }
 
+    const AnyRegister &dataTyped() const {
+        JS_ASSERT(hasTyped());
+        return *data.typed.addr();
+    }
+    const ValueOperand &dataValue() const {
+        JS_ASSERT(hasValue());
+        return *data.value.addr();
+    }
+
   public:
 
     TypedOrValueRegister()
       : type_(MIRType_None)
     {}
 
     TypedOrValueRegister(MIRType type, AnyRegister reg)
       : type_(type)
@@ -188,21 +197,21 @@ class TypedOrValueRegister
     bool hasTyped() const {
         return type() != MIRType_None && type() != MIRType_Value;
     }
 
     bool hasValue() const {
         return type() == MIRType_Value;
     }
 
-    AnyRegister typedReg() {
+    AnyRegister typedReg() const {
         return dataTyped();
     }
 
-    ValueOperand valueReg() {
+    ValueOperand valueReg() const {
         return dataValue();
     }
 
     AnyRegister scratchReg() {
         if (hasValue())
             return AnyRegister(valueReg().scratchReg());
         return typedReg();
     }