Bug 1267163 - Port primitive value getprop stub to CacheIR. r=efaust
authorJan de Mooij <jdemooij@mozilla.com>
Thu, 06 Oct 2016 14:58:35 +0200
changeset 316824 71808c547e9c2877d2d24a534e0b608ed2c053c4
parent 316823 e00a24772316bfdd3f89f769653e4564271c8e2d
child 316825 c64ed9250f55a4de8a2d5b57f06a9ff49e33a394
push id32932
push userphilringnalda@gmail.com
push dateFri, 07 Oct 2016 03:24:25 +0000
treeherderautoland@7affb66131bb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersefaust
bugs1267163
milestone52.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 1267163 - Port primitive value getprop stub to CacheIR. r=efaust
js/src/jit/BaselineCacheIR.cpp
js/src/jit/BaselineInspector.cpp
js/src/jit/CacheIR.cpp
js/src/jit/CacheIR.h
js/src/jit/SharedIC.cpp
js/src/jit/SharedIC.h
js/src/jit/SharedICList.h
--- a/js/src/jit/BaselineCacheIR.cpp
+++ b/js/src/jit/BaselineCacheIR.cpp
@@ -690,16 +690,46 @@ BaselineCacheIRCompiler::emitGuardIsObje
     FailurePath* failure;
     if (!addFailurePath(&failure))
         return false;
     masm.branchTestObject(Assembler::NotEqual, input, failure->label());
     return true;
 }
 
 bool
+BaselineCacheIRCompiler::emitGuardType()
+{
+    ValueOperand input = allocator.useRegister(masm, reader.valOperandId());
+    JSValueType type = reader.valueType();
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    switch (type) {
+      case JSVAL_TYPE_STRING:
+        masm.branchTestString(Assembler::NotEqual, input, failure->label());
+        break;
+      case JSVAL_TYPE_SYMBOL:
+        masm.branchTestSymbol(Assembler::NotEqual, input, failure->label());
+        break;
+      case JSVAL_TYPE_DOUBLE:
+        masm.branchTestNumber(Assembler::NotEqual, input, failure->label());
+        break;
+      case JSVAL_TYPE_BOOLEAN:
+        masm.branchTestBoolean(Assembler::NotEqual, input, failure->label());
+        break;
+      default:
+        MOZ_CRASH("Unexpected type");
+    }
+
+    return true;
+}
+
+bool
 BaselineCacheIRCompiler::emitGuardShape()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     AutoScratchRegister scratch(allocator, masm);
 
     FailurePath* failure;
     if (!addFailurePath(&failure))
         return false;
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -809,19 +809,23 @@ BaselineInspector::commonSetPropFunction
     return true;
 }
 
 static MIRType
 GetCacheIRExpectedInputType(ICCacheIR_Monitored* stub)
 {
     CacheIRReader reader(stub->stubInfo());
 
-    // For now, all CacheIR stubs expect an object.
-    MOZ_ALWAYS_TRUE(reader.matchOp(CacheOp::GuardIsObject, ObjOperandId(0)));
-    return MIRType::Object;
+    if (reader.matchOp(CacheOp::GuardIsObject, ValOperandId(0)))
+        return MIRType::Object;
+    if (reader.matchOp(CacheOp::GuardType, ValOperandId(0))) {
+        JSValueType type = reader.valueType();
+        return MIRTypeFromValueType(type);
+    }
+    MOZ_CRASH("Unexpected instruction");
 }
 
 MIRType
 BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc)
 {
     if (!hasBaselineScript())
         return MIRType::Value;
 
@@ -865,20 +869,16 @@ BaselineInspector::expectedPropertyAcces
           case ICStub::GetElem_UnboxedPropertyName:
           case ICStub::GetElem_String:
           case ICStub::GetElem_Dense:
           case ICStub::GetElem_TypedArray:
           case ICStub::GetElem_UnboxedArray:
             stubType = MIRType::Object;
             break;
 
-          case ICStub::GetProp_Primitive:
-            stubType = MIRTypeFromValueType(stub->toGetProp_Primitive()->primitiveType());
-            break;
-
           case ICStub::GetProp_StringLength:
             stubType = MIRType::String;
             break;
 
           case ICStub::CacheIR_Monitored:
             stubType = GetCacheIRExpectedInputType(stub->toCacheIR_Monitored());
             if (stubType == MIRType::Value)
                 return MIRType::Value;
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -63,18 +63,22 @@ GetPropIRGenerator::tryAttachStub(Maybe<
         if (!emitted_ && !tryAttachUnboxed(*writer, obj, objId))
             return false;
         if (!emitted_ && !tryAttachUnboxedExpando(*writer, obj, objId))
             return false;
         if (!emitted_ && !tryAttachTypedObject(*writer, obj, objId))
             return false;
         if (!emitted_ && !tryAttachModuleNamespace(*writer, obj, objId))
             return false;
+        return true;
     }
 
+    if (!emitted_ && !tryAttachPrimitive(*writer, valId))
+        return false;
+
     return true;
 }
 
 static bool
 IsCacheableNoProperty(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape, jsid id,
                       jsbytecode* pc)
 {
     if (shape)
@@ -410,8 +414,58 @@ GetPropIRGenerator::tryAttachModuleNames
 
     // Check for the specific namespace object.
     writer.guardSpecificObject(objId, ns);
 
     ObjOperandId envId = writer.loadObject(env);
     EmitLoadSlotResult(writer, envId, env, shape);
     return true;
 }
+
+bool
+GetPropIRGenerator::tryAttachPrimitive(CacheIRWriter& writer, ValOperandId valId)
+{
+    MOZ_ASSERT(!emitted_);
+
+    JSValueType primitiveType;
+    RootedNativeObject proto(cx_);
+    if (val_.isString()) {
+        if (name_ == cx_->names().length) {
+            // String length is special-cased, see js::GetProperty.
+            return true;
+        }
+        primitiveType = JSVAL_TYPE_STRING;
+        proto = MaybeNativeObject(GetBuiltinPrototypePure(cx_->global(), JSProto_String));
+    } else if (val_.isNumber()) {
+        primitiveType = JSVAL_TYPE_DOUBLE;
+        proto = MaybeNativeObject(GetBuiltinPrototypePure(cx_->global(), JSProto_Number));
+    } else if (val_.isBoolean()) {
+        primitiveType = JSVAL_TYPE_BOOLEAN;
+        proto = MaybeNativeObject(GetBuiltinPrototypePure(cx_->global(), JSProto_Boolean));
+    } else if (val_.isSymbol()) {
+        primitiveType = JSVAL_TYPE_SYMBOL;
+        proto = MaybeNativeObject(GetBuiltinPrototypePure(cx_->global(), JSProto_Symbol));
+    } else {
+        MOZ_ASSERT(val_.isNullOrUndefined() || val_.isMagic());
+        return true;
+    }
+    if (!proto)
+        return true;
+
+    // Instantiate this property, for use during Ion compilation.
+    RootedId id(cx_, NameToId(name_));
+    if (IsIonEnabled(cx_))
+        EnsureTrackPropertyTypes(cx_, proto, id);
+
+    // For now, only look for properties directly set on the prototype.
+    Shape* shape = proto->lookup(cx_, id);
+    if (!shape || !shape->hasSlot() || !shape->hasDefaultGetter())
+        return true;
+
+    writer.guardType(valId, primitiveType);
+
+    ObjOperandId protoId = writer.loadObject(proto);
+    writer.guardShape(protoId, proto->lastProperty());
+    EmitLoadSlotResult(writer, protoId, proto, shape);
+
+    emitted_ = true;
+    return true;
+}
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -75,16 +75,17 @@ class ObjOperandId : public OperandId
     explicit ObjOperandId(uint16_t id) : OperandId(id) {}
 
     bool operator==(const ObjOperandId& other) const { return id_ == other.id_; }
     bool operator!=(const ObjOperandId& other) const { return id_ != other.id_; }
 };
 
 #define CACHE_IR_OPS(_)                   \
     _(GuardIsObject)                      \
+    _(GuardType)                          \
     _(GuardShape)                         \
     _(GuardGroup)                         \
     _(GuardProto)                         \
     _(GuardClass)                         \
     _(GuardSpecificObject)                \
     _(GuardNoDetachedTypedObjects)        \
     _(GuardNoUnboxedExpando)              \
     _(GuardAndLoadUnboxedExpando)         \
@@ -242,16 +243,21 @@ class MOZ_RAII CacheIRWriter
     uint32_t codeLength() const {
         return buffer_.length();
     }
 
     ObjOperandId guardIsObject(ValOperandId val) {
         writeOpWithOperandId(CacheOp::GuardIsObject, val);
         return ObjOperandId(val.id());
     }
+    void guardType(ValOperandId val, JSValueType type) {
+        writeOpWithOperandId(CacheOp::GuardType, val);
+        MOZ_ASSERT(uint32_t(type) <= UINT8_MAX);
+        buffer_.writeByte(uint32_t(type));
+    }
     void guardShape(ObjOperandId obj, Shape* shape) {
         writeOpWithOperandId(CacheOp::GuardShape, obj);
         addStubWord(uintptr_t(shape), StubField::GCType::Shape);
     }
     void guardGroup(ObjOperandId obj, ObjectGroup* group) {
         writeOpWithOperandId(CacheOp::GuardGroup, obj);
         addStubWord(uintptr_t(group), StubField::GCType::ObjectGroup);
     }
@@ -417,16 +423,18 @@ class MOZ_RAII GetPropIRGenerator
                                               ObjOperandId objId);
     MOZ_MUST_USE bool tryAttachTypedObject(CacheIRWriter& writer, HandleObject obj,
                                            ObjOperandId objId);
     MOZ_MUST_USE bool tryAttachObjectLength(CacheIRWriter& writer, HandleObject obj,
                                             ObjOperandId objId);
     MOZ_MUST_USE bool tryAttachModuleNamespace(CacheIRWriter& writer, HandleObject obj,
                                                ObjOperandId objId);
 
+    MOZ_MUST_USE bool tryAttachPrimitive(CacheIRWriter& writer, ValOperandId valId);
+
     GetPropIRGenerator(const GetPropIRGenerator&) = delete;
     GetPropIRGenerator& operator=(const GetPropIRGenerator&) = delete;
 
   public:
     GetPropIRGenerator(JSContext* cx, jsbytecode* pc, HandleValue val, HandlePropertyName name,
                        MutableHandleValue res);
 
     bool emitted() const { return emitted_; }
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -395,21 +395,16 @@ ICStub::trace(JSTracer* trc)
       case ICStub::GetName_Env6:
         static_cast<ICGetName_Env<6>*>(this)->traceEnvironments(trc);
         break;
       case ICStub::GetIntrinsic_Constant: {
         ICGetIntrinsic_Constant* constantStub = toGetIntrinsic_Constant();
         TraceEdge(trc, &constantStub->value(), "baseline-getintrinsic-constant-value");
         break;
       }
-      case ICStub::GetProp_Primitive: {
-        ICGetProp_Primitive* propStub = toGetProp_Primitive();
-        TraceEdge(trc, &propStub->protoShape(), "baseline-getprop-primitive-stub-shape");
-        break;
-      }
       case ICStub::GetProp_CallDOMProxyNative:
       case ICStub::GetProp_CallDOMProxyWithGenerationNative: {
         ICGetPropCallDOMProxyNativeStub* propStub;
         if (kind() == ICStub::GetProp_CallDOMProxyNative)
             propStub = toGetProp_CallDOMProxyNative();
         else
             propStub = toGetProp_CallDOMProxyWithGenerationNative();
         propStub->receiverGuard().trace(trc);
@@ -2539,71 +2534,16 @@ TryAttachNativeGetAccessorPropStub(JSCon
     }
     if (!newStub)
         return false;
     stub->addNewStub(newStub);
     *attached = true;
     return true;
 }
 
-static bool
-TryAttachPrimitiveGetPropStub(JSContext* cx, SharedStubInfo* info,
-                              ICGetProp_Fallback* stub, HandlePropertyName name,
-                              HandleValue val, HandleValue res, bool* attached)
-{
-    MOZ_ASSERT(!*attached);
-
-    JSValueType primitiveType;
-    RootedNativeObject proto(cx);
-    Rooted<GlobalObject*> global(cx, &info->script()->global());
-    if (val.isString()) {
-        primitiveType = JSVAL_TYPE_STRING;
-        proto = GlobalObject::getOrCreateStringPrototype(cx, global);
-    } else if (val.isSymbol()) {
-        primitiveType = JSVAL_TYPE_SYMBOL;
-        proto = GlobalObject::getOrCreateSymbolPrototype(cx, global);
-    } else if (val.isNumber()) {
-        primitiveType = JSVAL_TYPE_DOUBLE;
-        proto = GlobalObject::getOrCreateNumberPrototype(cx, global);
-    } else {
-        MOZ_ASSERT(val.isBoolean());
-        primitiveType = JSVAL_TYPE_BOOLEAN;
-        proto = GlobalObject::getOrCreateBooleanPrototype(cx, global);
-    }
-    if (!proto)
-        return false;
-
-    // Instantiate this property, for use during Ion compilation.
-    RootedId id(cx, NameToId(name));
-    if (IsIonEnabled(cx))
-        EnsureTrackPropertyTypes(cx, proto, id);
-
-    // For now, only look for properties directly set on the prototype.
-    RootedShape shape(cx, proto->lookup(cx, id));
-    if (!shape || !shape->hasSlot() || !shape->hasDefaultGetter())
-        return true;
-
-    bool isFixedSlot;
-    uint32_t offset;
-    GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
-
-    ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
-
-    JitSpew(JitSpew_BaselineIC, "  Generating GetProp_Primitive stub");
-    ICGetProp_Primitive::Compiler compiler(cx, info->engine(), monitorStub, primitiveType, proto,
-                                           isFixedSlot, offset);
-    ICStub* newStub = compiler.getStub(compiler.getStubSpace(info->outerScript(cx)));
-    if (!newStub)
-        return false;
-
-    stub->addNewStub(newStub);
-    *attached = true;
-    return true;
-}
-
 bool
 CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, PropertyName* name,
                        JSObject** lastProto, size_t* protoChainDepthOut)
 {
     size_t depth = 0;
     JSObject* curObj = obj;
     while (curObj) {
         if (curObj->isNative()) {
@@ -2772,23 +2712,16 @@ DoGetPropFallback(JSContext* cx, void* p
 
     if (!TryAttachMagicArgumentsGetPropStub(cx, &info, stub, name, val,
                                             res, &attached))
         return false;
     if (attached)
         return true;
 
 
-    if (val.isString() || val.isNumber() || val.isBoolean()) {
-        if (!TryAttachPrimitiveGetPropStub(cx, &info, stub, name, val, res, &attached))
-            return false;
-        if (attached)
-            return true;
-    }
-
     MOZ_ASSERT(!attached);
     if (!isTemporarilyUnoptimizable)
         stub->noteUnoptimizableAccess();
 
     return true;
 }
 
 typedef bool (*DoGetPropFallbackFn)(JSContext*, void*, ICGetProp_Fallback*,
@@ -2863,63 +2796,16 @@ ICGetProp_StringLength::Compiler::genera
     EmitReturnFromIC(masm);
 
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
-bool
-ICGetProp_Primitive::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure;
-    switch (primitiveType_) {
-      case JSVAL_TYPE_STRING:
-        masm.branchTestString(Assembler::NotEqual, R0, &failure);
-        break;
-      case JSVAL_TYPE_SYMBOL:
-        masm.branchTestSymbol(Assembler::NotEqual, R0, &failure);
-        break;
-      case JSVAL_TYPE_DOUBLE: // Also used for int32.
-        masm.branchTestNumber(Assembler::NotEqual, R0, &failure);
-        break;
-      case JSVAL_TYPE_BOOLEAN:
-        masm.branchTestBoolean(Assembler::NotEqual, R0, &failure);
-        break;
-      default:
-        MOZ_CRASH("unexpected type");
-    }
-
-    AllocatableGeneralRegisterSet regs(availableGeneralRegs(1));
-    Register holderReg = regs.takeAny();
-    Register scratchReg = regs.takeAny();
-
-    // Verify the shape of the prototype.
-    masm.movePtr(ImmGCPtr(prototype_.get()), holderReg);
-
-    Address shapeAddr(ICStubReg, ICGetProp_Primitive::offsetOfProtoShape());
-    masm.loadPtr(Address(holderReg, ShapedObject::offsetOfShape()), scratchReg);
-    masm.branchPtr(Assembler::NotEqual, shapeAddr, scratchReg, &failure);
-
-    if (!isFixedSlot_)
-        masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg);
-
-    masm.load32(Address(ICStubReg, ICGetProp_Primitive::offsetOfOffset()), scratchReg);
-    masm.loadValue(BaseIndex(holderReg, scratchReg, TimesOne), R0);
-
-    // Enter type monitor IC to type-check result.
-    EmitEnterTypeMonitorIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
 ICGetPropNativeStub*
 ICGetPropNativeCompiler::getStub(ICStubSpace* space)
 {
     ReceiverGuard guard(obj_);
 
     switch (kind) {
       case ICStub::GetName_Global: {
         MOZ_ASSERT(obj_ != holder_);
@@ -3699,27 +3585,16 @@ BaselineScript::noteAccessedGetter(uint3
 {
     ICEntry& entry = icEntryFromPCOffset(pcOffset);
     ICFallbackStub* stub = entry.fallbackStub();
 
     if (stub->isGetProp_Fallback())
         stub->toGetProp_Fallback()->noteAccessedGetter();
 }
 
-ICGetProp_Primitive::ICGetProp_Primitive(JitCode* stubCode, ICStub* firstMonitorStub,
-                                         JSValueType primitiveType, Shape* protoShape,
-                                         uint32_t offset)
-  : ICMonitoredStub(GetProp_Primitive, stubCode, firstMonitorStub),
-    protoShape_(protoShape),
-    offset_(offset)
-{
-    extra_ = uint16_t(primitiveType);
-    MOZ_ASSERT(JSValueType(extra_) == primitiveType);
-}
-
 ICGetPropNativeStub::ICGetPropNativeStub(ICStub::Kind kind, JitCode* stubCode,
                                          ICStub* firstMonitorStub,
                                          ReceiverGuard guard, uint32_t offset)
   : ICMonitoredStub(kind, stubCode, firstMonitorStub),
     receiverGuard_(guard),
     offset_(offset)
 { }
 
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -2369,84 +2369,16 @@ class ICGetProp_Generic : public ICMonit
         {}
 
         ICStub* getStub(ICStubSpace* space) {
             return newStub<ICGetProp_Generic>(space, getStubCode(), firstMonitorStub_);
         }
     };
 };
 
-// Stub for accessing a property on a primitive's prototype.
-class ICGetProp_Primitive : public ICMonitoredStub
-{
-    friend class ICStubSpace;
-
-  protected: // Protected to silence Clang warning.
-    // Shape of String.prototype/Number.prototype to check for.
-    GCPtrShape protoShape_;
-
-    // Fixed or dynamic slot offset.
-    uint32_t offset_;
-
-    ICGetProp_Primitive(JitCode* stubCode, ICStub* firstMonitorStub, JSValueType primitiveType,
-                        Shape* protoShape, uint32_t offset);
-
-  public:
-    GCPtrShape& protoShape() {
-        return protoShape_;
-    }
-    JSValueType primitiveType() const {
-        return JSValueType(extra_);
-    }
-
-    static size_t offsetOfProtoShape() {
-        return offsetof(ICGetProp_Primitive, protoShape_);
-    }
-
-    static size_t offsetOfOffset() {
-        return offsetof(ICGetProp_Primitive, offset_);
-    }
-
-    class Compiler : public ICStubCompiler {
-        ICStub* firstMonitorStub_;
-        JSValueType primitiveType_;
-        RootedObject prototype_;
-        bool isFixedSlot_;
-        uint32_t offset_;
-
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
-
-      protected:
-        virtual int32_t getKey() const {
-            static_assert(sizeof(JSValueType) == 1, "JSValueType should fit in one byte");
-            return static_cast<int32_t>(engine_) |
-                  (static_cast<int32_t>(kind) << 1) |
-                  (static_cast<int32_t>(isFixedSlot_) << 17) |
-                  (static_cast<int32_t>(primitiveType_) << 25);
-        }
-
-      public:
-        Compiler(JSContext* cx, Engine engine, ICStub* firstMonitorStub, JSValueType primitiveType,
-                 HandleObject prototype, bool isFixedSlot, uint32_t offset)
-          : ICStubCompiler(cx, ICStub::GetProp_Primitive, engine),
-            firstMonitorStub_(firstMonitorStub),
-            primitiveType_(primitiveType),
-            prototype_(cx, prototype),
-            isFixedSlot_(isFixedSlot),
-            offset_(offset)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) {
-            RootedShape protoShape(cx, prototype_->as<NativeObject>().lastProperty());
-            return newStub<ICGetProp_Primitive>(space, getStubCode(), firstMonitorStub_,
-                                                primitiveType_, protoShape, offset_);
-        }
-    };
-};
-
 // Stub for accessing a string's length.
 class ICGetProp_StringLength : public ICStub
 {
     friend class ICStubSpace;
 
     explicit ICGetProp_StringLength(JitCode* stubCode)
       : ICStub(GetProp_StringLength, stubCode)
     {}
--- a/js/src/jit/SharedICList.h
+++ b/js/src/jit/SharedICList.h
@@ -30,17 +30,16 @@ namespace jit {
     _(Compare_NumberWithUndefined)               \
     _(Compare_String)                            \
     _(Compare_Boolean)                           \
     _(Compare_Object)                            \
     _(Compare_ObjectWithUndefined)               \
     _(Compare_Int32WithBoolean)                  \
                                                  \
     _(GetProp_Fallback)                          \
-    _(GetProp_Primitive)                         \
     _(GetProp_StringLength)                      \
     _(GetProp_CallScripted)                      \
     _(GetProp_CallNative)                        \
     _(GetProp_CallNativeGlobal)                  \
     _(GetProp_CallDOMProxyNative)                \
     _(GetProp_CallDOMProxyWithGenerationNative)  \
     _(GetProp_DOMProxyShadowed)                  \
     _(GetProp_ArgumentsLength)                   \