Bug 1334187 - Port In_Native/In_NativePrototype from BaselineIC to CacheIR. r?jandem draft
authorTed Campbell <tcampbell@mozilla.com>
Thu, 02 Feb 2017 02:41:28 -0500
changeset 479742 e3966729e0617ecdf97014e7c8633396a6585039
parent 479741 1c6c54ee868f32e89bab7dd1978e476380869716
child 479743 0bb1140d4e9c2d17fc9dbc6c3b1359bf7cee4274
push id44342
push userbmo:tcampbell@mozilla.com
push dateTue, 07 Feb 2017 05:31:18 +0000
reviewersjandem
bugs1334187
milestone54.0a1
Bug 1334187 - Port In_Native/In_NativePrototype from BaselineIC to CacheIR. r?jandem MozReview-Commit-ID: 1fBSdgDHQNB
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
js/src/jit/BaselineICList.h
js/src/jit/CacheIR.cpp
js/src/jit/CacheIR.h
js/src/jit/CacheIRCompiler.cpp
js/src/jit/CacheIRCompiler.h
js/src/jit/SharedIC.cpp
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1958,56 +1958,16 @@ ICSetElem_TypedArray::Compiler::generate
     return true;
 }
 
 //
 // In_Fallback
 //
 
 static bool
-TryAttachNativeInStub(JSContext* cx, HandleScript outerScript, ICIn_Fallback* stub,
-                      HandleValue key, HandleObject obj, bool* attached)
-{
-    MOZ_ASSERT(!*attached);
-
-    RootedId id(cx);
-    if (!IsOptimizableElementPropertyName(cx, key, &id))
-        return true;
-
-    RootedPropertyName name(cx, JSID_TO_ATOM(id)->asPropertyName());
-    Rooted<PropertyResult> prop(cx);
-    RootedObject holder(cx);
-    if (!LookupPropertyPure(cx, obj, id, holder.address(), prop.address()))
-        return true;
-
-    if (prop.isNonNativeProperty()) {
-        MOZ_ASSERT(!IsCacheableProtoChain(obj, holder, false));
-        return true;
-    }
-
-    RootedShape shape(cx, prop.maybeShape());
-    if (IsCacheableGetPropReadSlot(obj, holder, shape)) {
-        ICStub::Kind kind = (obj == holder) ? ICStub::In_Native
-                                            : ICStub::In_NativePrototype;
-        JitSpew(JitSpew_BaselineIC, "  Generating In(Native %s) stub",
-                    (obj == holder) ? "direct" : "prototype");
-        ICInNativeCompiler compiler(cx, kind, obj, holder, name);
-        ICStub* newStub = compiler.getStub(compiler.getStubSpace(outerScript));
-        if (!newStub)
-            return false;
-
-        *attached = true;
-        stub->addNewStub(newStub);
-        return true;
-    }
-
-    return true;
-}
-
-static bool
 TryAttachNativeInDoesNotExistStub(JSContext* cx, HandleScript outerScript,
                                   ICIn_Fallback* stub, HandleValue key,
                                   HandleObject obj, bool* attached)
 {
     MOZ_ASSERT(!*attached);
 
     RootedId id(cx);
     if (!IsOptimizableElementPropertyName(cx, key, &id))
@@ -2080,22 +2040,17 @@ DoInFallback(JSContext* cx, BaselineFram
         return true;
 
     if (attached)
         return true;
 
     if (obj->isNative()) {
         RootedScript script(cx, frame->script());
         bool attached = false;
-        if (cond) {
-            if (!TryAttachNativeInStub(cx, script, stub, key, obj, &attached))
-                return false;
-            if (attached)
-                return true;
-        } else {
+        if (!cond) {
             if (!TryAttachNativeInDoesNotExistStub(cx, script, stub, key, obj, &attached))
                 return false;
             if (attached)
                 return true;
         }
     }
 
     return true;
@@ -2121,63 +2076,16 @@ ICIn_Fallback::Compiler::generateStubCod
     masm.pushValue(R1);
     masm.pushValue(R0);
     masm.push(ICStubReg);
     pushStubPayload(masm, R0.scratchReg());
 
     return tailCallVM(DoInFallbackInfo, masm);
 }
 
-bool
-ICInNativeCompiler::generateStubCode(MacroAssembler& masm)
-{
-    MOZ_ASSERT(engine_ == Engine::Baseline);
-
-    Label failure, failurePopR0Scratch;
-
-    masm.branchTestString(Assembler::NotEqual, R0, &failure);
-    masm.branchTestObject(Assembler::NotEqual, R1, &failure);
-
-    AllocatableGeneralRegisterSet regs(availableGeneralRegs(2));
-    Register scratch = regs.takeAny();
-
-    // Check key identity.
-    Register strExtract = masm.extractString(R0, ExtractTemp0);
-    masm.loadPtr(Address(ICStubReg, ICInNativeStub::offsetOfName()), scratch);
-    masm.branchPtr(Assembler::NotEqual, strExtract, scratch, &failure);
-
-    // Unbox and shape guard object.
-    Register objReg = masm.extractObject(R1, ExtractTemp0);
-    masm.loadPtr(Address(ICStubReg, ICInNativeStub::offsetOfShape()), scratch);
-    masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure);
-
-    if (kind == ICStub::In_NativePrototype) {
-        // Shape guard holder. Use R0 scrachReg since on x86 there're not enough registers.
-        Register holderReg = R0.scratchReg();
-        masm.push(R0.scratchReg());
-        masm.loadPtr(Address(ICStubReg, ICIn_NativePrototype::offsetOfHolder()),
-                     holderReg);
-        masm.loadPtr(Address(ICStubReg, ICIn_NativePrototype::offsetOfHolderShape()),
-                     scratch);
-        masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failurePopR0Scratch);
-        masm.addToStackPtr(Imm32(sizeof(size_t)));
-    }
-
-    masm.moveValue(BooleanValue(true), R0);
-
-    EmitReturnFromIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failurePopR0Scratch);
-    masm.pop(R0.scratchReg());
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
 ICStub*
 ICInNativeDoesNotExistCompiler::getStub(ICStubSpace* space)
 {
     Rooted<ShapeVector> shapes(cx, ShapeVector(cx));
     if (!shapes.append(obj_->as<NativeObject>().lastProperty()))
         return nullptr;
 
     if (!GetProtoShapes(obj_, protoChainDepth_, &shapes))
@@ -5556,31 +5464,16 @@ ICSetElem_TypedArray::ICSetElem_TypedArr
   : ICStub(SetElem_TypedArray, stubCode),
     shape_(shape)
 {
     extra_ = uint8_t(type);
     MOZ_ASSERT(extra_ == type);
     extra_ |= (static_cast<uint16_t>(expectOutOfBounds) << 8);
 }
 
-ICInNativeStub::ICInNativeStub(ICStub::Kind kind, JitCode* stubCode, HandleShape shape,
-                               HandlePropertyName name)
-  : ICStub(kind, stubCode),
-    shape_(shape),
-    name_(name)
-{ }
-
-ICIn_NativePrototype::ICIn_NativePrototype(JitCode* stubCode, HandleShape shape,
-                                           HandlePropertyName name, HandleObject holder,
-                                           HandleShape holderShape)
-  : ICInNativeStub(In_NativePrototype, stubCode, shape, name),
-    holder_(holder),
-    holderShape_(holderShape)
-{ }
-
 ICIn_NativeDoesNotExist::ICIn_NativeDoesNotExist(JitCode* stubCode, size_t protoChainDepth,
                                                  HandlePropertyName name)
   : ICStub(In_NativeDoesNotExist, stubCode),
     name_(name)
 {
     MOZ_ASSERT(protoChainDepth <= MAX_PROTO_CHAIN_DEPTH);
     extra_ = protoChainDepth;
 }
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -708,113 +708,16 @@ class ICIn_Fallback : public ICFallbackS
         { }
 
         ICStub* getStub(ICStubSpace* space) {
             return newStub<ICIn_Fallback>(space, getStubCode());
         }
     };
 };
 
-// Base class for In_Native and In_NativePrototype stubs.
-class ICInNativeStub : public ICStub
-{
-    GCPtrShape shape_;
-    GCPtrPropertyName name_;
-
-  protected:
-    ICInNativeStub(ICStub::Kind kind, JitCode* stubCode, HandleShape shape,
-                   HandlePropertyName name);
-
-  public:
-    GCPtrShape& shape() {
-        return shape_;
-    }
-    static size_t offsetOfShape() {
-        return offsetof(ICInNativeStub, shape_);
-    }
-
-    GCPtrPropertyName& name() {
-        return name_;
-    }
-    static size_t offsetOfName() {
-        return offsetof(ICInNativeStub, name_);
-    }
-};
-
-// Stub for confirming an own property on a native object.
-class ICIn_Native : public ICInNativeStub
-{
-    friend class ICStubSpace;
-
-    ICIn_Native(JitCode* stubCode, HandleShape shape, HandlePropertyName name)
-      : ICInNativeStub(In_Native, stubCode, shape, name)
-    {}
-};
-
-// Stub for confirming a property on a native object's prototype. Note that due to
-// the shape teleporting optimization, we only have to guard on the object's shape
-// and the holder's shape.
-class ICIn_NativePrototype : public ICInNativeStub
-{
-    friend class ICStubSpace;
-
-    GCPtrObject holder_;
-    GCPtrShape holderShape_;
-
-    ICIn_NativePrototype(JitCode* stubCode, HandleShape shape, HandlePropertyName name,
-                         HandleObject holder, HandleShape holderShape);
-
-  public:
-    GCPtrObject& holder() {
-        return holder_;
-    }
-    GCPtrShape& holderShape() {
-        return holderShape_;
-    }
-    static size_t offsetOfHolder() {
-        return offsetof(ICIn_NativePrototype, holder_);
-    }
-    static size_t offsetOfHolderShape() {
-        return offsetof(ICIn_NativePrototype, holderShape_);
-    }
-};
-
-// Compiler for In_Native and In_NativePrototype stubs.
-class ICInNativeCompiler : public ICStubCompiler
-{
-    RootedObject obj_;
-    RootedObject holder_;
-    RootedPropertyName name_;
-
-    MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
-
-  public:
-    ICInNativeCompiler(JSContext* cx, ICStub::Kind kind, HandleObject obj, HandleObject holder,
-                       HandlePropertyName name)
-      : ICStubCompiler(cx, kind, Engine::Baseline),
-        obj_(cx, obj),
-        holder_(cx, holder),
-        name_(cx, name)
-    {}
-
-    ICStub* getStub(ICStubSpace* space) {
-        RootedShape shape(cx, obj_->as<NativeObject>().lastProperty());
-        if (kind == ICStub::In_Native) {
-            MOZ_ASSERT(obj_ == holder_);
-            return newStub<ICIn_Native>(space, getStubCode(), shape, name_);
-        }
-
-        MOZ_ASSERT(obj_ != holder_);
-        MOZ_ASSERT(kind == ICStub::In_NativePrototype);
-        RootedShape holderShape(cx, holder_->as<NativeObject>().lastProperty());
-        return newStub<ICIn_NativePrototype>(space, getStubCode(), shape, name_, holder_,
-                                             holderShape);
-    }
-};
-
 template <size_t ProtoChainDepth> class ICIn_NativeDoesNotExistImpl;
 
 class ICIn_NativeDoesNotExist : public ICStub
 {
     friend class ICStubSpace;
 
     GCPtrPropertyName name_;
 
--- a/js/src/jit/BaselineICList.h
+++ b/js/src/jit/BaselineICList.h
@@ -51,18 +51,16 @@ namespace jit {
     _(GetElem_Fallback)                          \
                                                  \
     _(SetElem_Fallback)                          \
     _(SetElem_DenseOrUnboxedArray)               \
     _(SetElem_DenseOrUnboxedArrayAdd)            \
     _(SetElem_TypedArray)                        \
                                                  \
     _(In_Fallback)                               \
-    _(In_Native)                                 \
-    _(In_NativePrototype)                        \
     _(In_NativeDoesNotExist)                     \
                                                  \
     _(GetName_Fallback)                          \
                                                  \
     _(BindName_Fallback)                         \
                                                  \
     _(GetIntrinsic_Fallback)                     \
     _(GetIntrinsic_Constant)                     \
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -360,56 +360,65 @@ TestMatchingReceiver(CacheIRWriter& writ
     } else {
         Shape* shape = obj->maybeShape();
         MOZ_ASSERT(shape);
         writer.guardShape(objId, shape);
     }
 }
 
 static void
-EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
-                   Shape* shape, ObjOperandId objId)
+EmitReadSlotGuard(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
+                  Shape* shape, ObjOperandId objId, Maybe<ObjOperandId>* holderId)
 {
     Maybe<ObjOperandId> expandoId;
     TestMatchingReceiver(writer, obj, shape, objId, &expandoId);
 
-    ObjOperandId holderId;
     if (obj != holder) {
         GeneratePrototypeGuards(writer, obj, holder, objId);
 
         if (holder) {
             // Guard on the holder's shape.
-            holderId = writer.loadObject(holder);
-            writer.guardShape(holderId, holder->as<NativeObject>().lastProperty());
+            holderId->emplace(writer.loadObject(holder));
+            writer.guardShape(holderId->ref(), holder->as<NativeObject>().lastProperty());
         } else {
             // The property does not exist. Guard on everything in the prototype
             // chain. This is guaranteed to see only Native objects because of
             // CanAttachNativeGetProp().
             JSObject* proto = obj->taggedProto().toObjectOrNull();
             ObjOperandId lastObjId = objId;
             while (proto) {
                 ObjOperandId protoId = writer.loadProto(lastObjId);
                 writer.guardShape(protoId, proto->as<NativeObject>().lastProperty());
                 proto = proto->staticPrototype();
                 lastObjId = protoId;
             }
         }
     } else if (obj->is<UnboxedPlainObject>()) {
-        holder = obj->as<UnboxedPlainObject>().maybeExpando();
-        holderId = *expandoId;
+        holderId->emplace(*expandoId);
     } else {
-        holderId = objId;
+        holderId->emplace(objId);
     }
+}
+
+static void
+EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
+                   Shape* shape, ObjOperandId objId)
+{
+    Maybe<ObjOperandId> holderId;
+    EmitReadSlotGuard(writer, obj, holder, shape, objId, &holderId);
+
+    if (obj == holder && obj->is<UnboxedPlainObject>())
+        holder = obj->as<UnboxedPlainObject>().maybeExpando();
 
     // Slot access.
     if (holder) {
-        MOZ_ASSERT(holderId.valid());
-        EmitLoadSlotResult(writer, holderId, &holder->as<NativeObject>(), shape);
+        MOZ_ASSERT(holderId->valid());
+        EmitLoadSlotResult(writer, *holderId, &holder->as<NativeObject>(), shape);
     } else {
-        MOZ_ASSERT(!holderId.valid());
+        MOZ_ASSERT(holderId.isNothing());
         writer.loadUndefinedResult();
     }
 }
 
 static void
 EmitReadSlotReturn(CacheIRWriter& writer, JSObject*, JSObject* holder, Shape* shape)
 {
     // Slot access.
@@ -1579,26 +1588,63 @@ InIRGenerator::tryAttachDenseIn(uint32_t
 
     writer.guardShape(objId, obj->as<NativeObject>().lastProperty());
     writer.loadDenseElementExistsResult(objId, indexId);
     writer.returnFromIC();
     return true;
 }
 
 bool
+InIRGenerator::tryAttachNativeIn(HandleId key, ValOperandId keyId,
+                                 HandleObject obj, ObjOperandId objId)
+{
+    PropertyResult prop;
+    JSObject* holder;
+    if (!LookupPropertyPure(cx_, obj, key, &holder, &prop))
+        return false;
+
+    if (prop.isNonNativeProperty())
+        return false;
+
+    if (!IsCacheableGetPropReadSlotForIonOrCacheIR(obj, holder, prop))
+        return false;
+
+    Maybe<ObjOperandId> holderId;
+    emitIdGuard(keyId, key);
+    EmitReadSlotGuard(writer, obj, holder, prop.maybeShape(), objId, &holderId);
+    writer.loadBooleanResult(true);
+    writer.returnFromIC();
+
+    return true;
+}
+
+bool
 InIRGenerator::tryAttachStub()
 {
     MOZ_ASSERT(cacheKind_ == CacheKind::In);
 
     AutoAssertNoPendingException aanpe(cx_);
 
     ValOperandId keyId(writer.setInputOperandId(0));
     ValOperandId valId(writer.setInputOperandId(1));
     ObjOperandId objId = writer.guardIsObject(valId);
 
+    RootedId id(cx_);
+    bool nameOrSymbol;
+    if (!ValueToNameOrSymbolId(cx_, key_, &id, &nameOrSymbol)) {
+        cx_->clearPendingException();
+        return false;
+    }
+
+    if (nameOrSymbol) {
+        if (tryAttachNativeIn(id, keyId, obj_, objId))
+            return true;
+        return false;
+    }
+
     uint32_t index;
     Int32OperandId indexId;
     if (maybeGuardInt32Index(key_, keyId, &index, &indexId)) {
         if (tryAttachDenseIn(index, indexId, obj_, objId))
             return true;
         return false;
     }
 
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -206,16 +206,17 @@ enum class CacheKind : uint8_t
     _(LoadFrameArgumentResult)            \
     _(LoadEnvironmentFixedSlotResult)     \
     _(LoadEnvironmentDynamicSlotResult)   \
     _(CallScriptedGetterResult)           \
     _(CallNativeGetterResult)             \
     _(CallProxyGetResult)                 \
     _(CallProxyGetByValueResult)          \
     _(LoadUndefinedResult)                \
+    _(LoadBooleanResult)                  \
                                           \
     _(TypeMonitorResult)                  \
     _(ReturnFromIC)
 
 enum class CacheOp {
 #define DEFINE_OP(op) op,
     CACHE_IR_OPS(DEFINE_OP)
 #undef DEFINE_OP
@@ -651,16 +652,20 @@ class MOZ_RAII CacheIRWriter : public JS
         writeOperandId(rhs);
     }
     void callSetArrayLength(ObjOperandId obj, bool strict, ValOperandId rhs) {
         writeOpWithOperandId(CacheOp::CallSetArrayLength, obj);
         buffer_.writeByte(uint32_t(strict));
         writeOperandId(rhs);
     }
 
+    void loadBooleanResult(bool val) {
+        writeOp(CacheOp::LoadBooleanResult);
+        buffer_.writeByte(uint32_t(val));
+    }
     void loadUndefinedResult() {
         writeOp(CacheOp::LoadUndefinedResult);
     }
     void loadFixedSlotResult(ObjOperandId obj, size_t offset) {
         writeOpWithOperandId(CacheOp::LoadFixedSlotResult, obj);
         addStubField(offset, StubField::Type::RawWord);
     }
     void loadDynamicSlotResult(ObjOperandId obj, size_t offset) {
@@ -1034,16 +1039,18 @@ class MOZ_RAII SetPropIRGenerator : publ
 // InIRGenerator generates CacheIR for a In IC.
 class MOZ_RAII InIRGenerator : public IRGenerator
 {
     HandleValue key_;
     HandleObject obj_;
 
     bool tryAttachDenseIn(uint32_t index, Int32OperandId indexId,
                           HandleObject obj, ObjOperandId objId);
+    bool tryAttachNativeIn(HandleId key, ValOperandId keyId,
+                           HandleObject obj, ObjOperandId objId);
 
   public:
     InIRGenerator(JSContext* cx, jsbytecode* pc, HandleValue key, HandleObject obj);
 
     bool tryAttachStub();
 };
 
 } // namespace jit
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -1465,16 +1465,30 @@ CacheIRCompiler::emitLoadUndefinedResult
     AutoOutputRegister output(*this);
     if (output.hasValue())
         masm.moveValue(UndefinedValue(), output.valueReg());
     else
         masm.assumeUnreachable("Should have monitored undefined result");
     return true;
 }
 
+bool
+CacheIRCompiler::emitLoadBooleanResult()
+{
+    AutoOutputRegister output(*this);
+    if (output.hasValue()) {
+        Value val = BooleanValue(reader.readBool());
+        masm.moveValue(val, output.valueReg());
+    }
+    else {
+        MOZ_CRASH("NYI: Typed LoadBooleanResult");
+    }
+    return true;
+}
+
 static void
 EmitStoreResult(MacroAssembler& masm, Register reg, JSValueType type,
                 const AutoOutputRegister& output)
 {
     if (output.hasValue()) {
         masm.tagValue(type, reg, output.valueReg());
         return;
     }
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -30,16 +30,17 @@ namespace jit {
     _(GuardNoDetachedTypedObjects)        \
     _(GuardNoDenseElements)               \
     _(GuardAndGetIndexFromString)         \
     _(LoadProto)                          \
     _(LoadEnclosingEnvironment)           \
     _(LoadDOMExpandoValue)                \
     _(LoadDOMExpandoValueIgnoreGeneration)\
     _(LoadUndefinedResult)                \
+    _(LoadBooleanResult)                  \
     _(LoadInt32ArrayLengthResult)         \
     _(LoadUnboxedArrayLengthResult)       \
     _(LoadArgumentsObjectLengthResult)    \
     _(LoadFunctionLengthResult)           \
     _(LoadStringLengthResult)             \
     _(LoadStringCharResult)               \
     _(LoadArgumentsObjectArgResult)       \
     _(LoadDenseElementResult)             \
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -309,30 +309,16 @@ ICStub::trace(JSTracer* trc)
         TraceEdge(trc, &updateStub->object(), "baseline-update-singleton");
         break;
       }
       case ICStub::TypeUpdate_ObjectGroup: {
         ICTypeUpdate_ObjectGroup* updateStub = toTypeUpdate_ObjectGroup();
         TraceEdge(trc, &updateStub->group(), "baseline-update-group");
         break;
       }
-      case ICStub::In_Native: {
-        ICIn_Native* inStub = toIn_Native();
-        TraceEdge(trc, &inStub->shape(), "baseline-innative-stub-shape");
-        TraceEdge(trc, &inStub->name(), "baseline-innative-stub-name");
-        break;
-      }
-      case ICStub::In_NativePrototype: {
-        ICIn_NativePrototype* inStub = toIn_NativePrototype();
-        TraceEdge(trc, &inStub->shape(), "baseline-innativeproto-stub-shape");
-        TraceEdge(trc, &inStub->name(), "baseline-innativeproto-stub-name");
-        TraceEdge(trc, &inStub->holder(), "baseline-innativeproto-stub-holder");
-        TraceEdge(trc, &inStub->holderShape(), "baseline-innativeproto-stub-holdershape");
-        break;
-      }
       case ICStub::In_NativeDoesNotExist: {
         ICIn_NativeDoesNotExist* inStub = toIn_NativeDoesNotExist();
         TraceEdge(trc, &inStub->name(), "baseline-innativedoesnotexist-stub-name");
         JS_STATIC_ASSERT(ICIn_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH == 8);
         switch (inStub->protoChainDepth()) {
           case 0: inStub->toImpl<0>()->traceShapes(trc); break;
           case 1: inStub->toImpl<1>()->traceShapes(trc); break;
           case 2: inStub->toImpl<2>()->traceShapes(trc); break;