diff --git a/js/src/jit-test/tests/cacheir/bug1420910.js b/js/src/jit-test/tests/cacheir/bug1420910.js new file mode 100644 index 000000000000..41d17376dd31 --- /dev/null +++ b/js/src/jit-test/tests/cacheir/bug1420910.js @@ -0,0 +1,35 @@ +// Testing InstanceOf IC. + +Array.prototype.sum = function() { + return this.reduce(( acc, cur ) => acc + cur, 0); +} + + +Iters = 20; + +function resultArray(fn, obj) { + res = new Array(); + for (var x = 0; x < Iters; x++) { + res.push(fn(obj) ? 1 : 0); + } + return res; +} + +// Ensure alteration of .prototype invalidates IC +function basic() {}; + +protoA = { prop1: "1"}; +basic.prototype = protoA; + +io1 = x => { return x instanceof basic; } + +var x = new basic(); +beforePrototypeModification = resultArray(io1,x).sum(); //Attach and test IC +assertEq(beforePrototypeModification,Iters); + +basic.prototype = {}; // Invalidate IC +afterPrototypeModification = resultArray(io1,x).sum(); //Test +assertEq(afterPrototypeModification,0); + +//Primitive LHS returns false. +assertEq(resultArray(io1,0).sum(),0); \ No newline at end of file diff --git a/js/src/jit/BaselineCacheIRCompiler.cpp b/js/src/jit/BaselineCacheIRCompiler.cpp index 3e20267e8bdf..e742ab618439 100644 --- a/js/src/jit/BaselineCacheIRCompiler.cpp +++ b/js/src/jit/BaselineCacheIRCompiler.cpp @@ -449,6 +449,34 @@ BaselineCacheIRCompiler::emitGuardXrayExpandoShapeAndDefaultProto() return true; } +bool +BaselineCacheIRCompiler::emitGuardFunctionPrototype() +{ + Register obj = allocator.useRegister(masm, reader.objOperandId()); + Register prototypeObject = allocator.useRegister(masm, reader.objOperandId()); + + // Allocate registers before the failure path to make sure they're registered + // by addFailurePath. + AutoScratchRegister scratch1(allocator, masm); + AutoScratchRegister scratch2(allocator, masm); + + FailurePath* failure; + if (!addFailurePath(&failure)) + return false; + + // Guard on the .prototype object. + masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch1); + masm.load32(Address(stubAddress(reader.stubOffset())), scratch2); + BaseValueIndex prototypeSlot(scratch1, scratch2); + masm.branchTestObject(Assembler::NotEqual, prototypeSlot, failure->label()); + masm.unboxObject(prototypeSlot, scratch1); + masm.branchPtr(Assembler::NotEqual, + prototypeObject, + scratch1, failure->label()); + + return true; +} + bool BaselineCacheIRCompiler::emitLoadFixedSlotResult() { @@ -2076,6 +2104,7 @@ BaselineCacheIRCompiler::init(CacheKind kind) case CacheKind::SetProp: case CacheKind::In: case CacheKind::HasOwn: + case CacheKind::InstanceOf: MOZ_ASSERT(numInputs == 2); allocator.initInputLocation(0, R0); allocator.initInputLocation(1, R1); diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 469c9c3ac4fb..9d59f3547f26 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -4292,46 +4292,34 @@ ICIteratorClose_Fallback::Compiler::generateStubCode(MacroAssembler& masm) static bool TryAttachInstanceOfStub(JSContext* cx, BaselineFrame* frame, ICInstanceOf_Fallback* stub, - HandleFunction fun, bool* attached) + HandleValue lhs, HandleValue rhs, bool* attached) { MOZ_ASSERT(!*attached); - if (fun->isBoundFunction()) - return true; + FallbackICSpew(cx, stub, "InstanceOf"); - // If the user has supplied their own @@hasInstance method we shouldn't - // clobber it. - if (!js::FunctionHasDefaultHasInstance(fun, cx->wellKnownSymbols())) - return true; + if (stub->state().maybeTransition()) + stub->discardStubs(cx); - // Refuse to optimize any function whose [[Prototype]] isn't - // Function.prototype. - if (!fun->hasStaticPrototype() || fun->hasUncacheableProto()) - return true; + if (stub->state().canAttachStub()) { + RootedScript script(cx, frame->script()); + jsbytecode* pc = stub->icEntry()->pc(script); - Value funProto = cx->global()->getPrototype(JSProto_Function); - if (funProto.isObject() && fun->staticPrototype() != &funProto.toObject()) - return true; + ICStubEngine engine = ICStubEngine::Baseline; + InstanceOfIRGenerator gen(cx, script, pc, stub->state().mode(), + lhs, + rhs); - Shape* shape = fun->lookupPure(cx->names().prototype); - if (!shape || !shape->isDataProperty()) - return true; + if (gen.tryAttachStub()) { + ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), + BaselineCacheIRStubKind::Regular, + engine, script, stub, attached); + if (newStub) + JitSpew(JitSpew_BaselineIC, " Attached InstanceOf CacheIR stub, attached is now %d", *attached); + } + if (!attached) + stub->state().trackNotAttached(); + } - uint32_t slot = shape->slot(); - MOZ_ASSERT(fun->numFixedSlots() == 0, "Stub code relies on this"); - - if (!fun->getSlot(slot).isObject()) - return true; - - JSObject* protoObject = &fun->getSlot(slot).toObject(); - - JitSpew(JitSpew_BaselineIC, " Generating InstanceOf(Function) stub"); - ICInstanceOf_Function::Compiler compiler(cx, fun->lastProperty(), protoObject, slot); - ICStub* newStub = compiler.getStub(compiler.getStubSpace(frame->script())); - if (!newStub) - return false; - - stub->addNewStub(newStub); - *attached = true; return true; } @@ -4362,12 +4350,8 @@ DoInstanceOfFallback(JSContext* cx, BaselineFrame* frame, ICInstanceOf_Fallback* // for use during Ion compilation. EnsureTrackPropertyTypes(cx, obj, NameToId(cx->names().prototype)); - if (stub->numOptimizedStubs() >= ICInstanceOf_Fallback::MAX_OPTIMIZED_STUBS) - return true; - - RootedFunction fun(cx, &obj->as()); bool attached = false; - if (!TryAttachInstanceOfStub(cx, frame, stub, fun, &attached)) + if (!TryAttachInstanceOfStub(cx, frame, stub, lhs, rhs, &attached)) return false; if (!attached) stub->noteUnoptimizableAccess(); @@ -4399,82 +4383,6 @@ ICInstanceOf_Fallback::Compiler::generateStubCode(MacroAssembler& masm) return tailCallVM(DoInstanceOfFallbackInfo, masm); } -bool -ICInstanceOf_Function::Compiler::generateStubCode(MacroAssembler& masm) -{ - MOZ_ASSERT(engine_ == Engine::Baseline); - - Label failure; - - // Ensure RHS is an object. - masm.branchTestObject(Assembler::NotEqual, R1, &failure); - Register rhsObj = masm.extractObject(R1, ExtractTemp0); - - // Allow using R1's type register as scratch. We have to restore it when - // we want to jump to the next stub. - Label failureRestoreR1; - AllocatableGeneralRegisterSet regs(availableGeneralRegs(1)); - regs.takeUnchecked(rhsObj); - - Register scratch1 = regs.takeAny(); - Register scratch2 = regs.takeAny(); - - // Shape guard. - masm.loadPtr(Address(ICStubReg, ICInstanceOf_Function::offsetOfShape()), scratch1); - masm.branchTestObjShape(Assembler::NotEqual, rhsObj, scratch1, &failureRestoreR1); - - // Guard on the .prototype object. - masm.loadPtr(Address(rhsObj, NativeObject::offsetOfSlots()), scratch1); - masm.load32(Address(ICStubReg, ICInstanceOf_Function::offsetOfSlot()), scratch2); - BaseValueIndex prototypeSlot(scratch1, scratch2); - masm.branchTestObject(Assembler::NotEqual, prototypeSlot, &failureRestoreR1); - masm.unboxObject(prototypeSlot, scratch1); - masm.branchPtr(Assembler::NotEqual, - Address(ICStubReg, ICInstanceOf_Function::offsetOfPrototypeObject()), - scratch1, &failureRestoreR1); - - // If LHS is a primitive, return false. - Label returnFalse, returnTrue; - masm.branchTestObject(Assembler::NotEqual, R0, &returnFalse); - - // LHS is an object. Load its proto. - masm.unboxObject(R0, scratch2); - masm.loadObjProto(scratch2, scratch2); - - { - // Walk the proto chain until we either reach the target object, - // nullptr or LazyProto. - Label loop; - masm.bind(&loop); - - masm.branchPtr(Assembler::Equal, scratch2, scratch1, &returnTrue); - masm.branchTestPtr(Assembler::Zero, scratch2, scratch2, &returnFalse); - - MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1); - masm.branchPtr(Assembler::Equal, scratch2, ImmWord(1), &failureRestoreR1); - - masm.loadObjProto(scratch2, scratch2); - masm.jump(&loop); - } - - EmitReturnFromIC(masm); - - masm.bind(&returnFalse); - masm.moveValue(BooleanValue(false), R0); - EmitReturnFromIC(masm); - - masm.bind(&returnTrue); - masm.moveValue(BooleanValue(true), R0); - EmitReturnFromIC(masm); - - masm.bind(&failureRestoreR1); - masm.tagValue(JSVAL_TYPE_OBJECT, rhsObj, R1); - - masm.bind(&failure); - EmitStubGuardFailure(masm); - return true; -} - // // TypeOf_Fallback // @@ -4667,14 +4575,6 @@ ICGetIntrinsic_Constant::ICGetIntrinsic_Constant(JitCode* stubCode, const Value& ICGetIntrinsic_Constant::~ICGetIntrinsic_Constant() { } -ICInstanceOf_Function::ICInstanceOf_Function(JitCode* stubCode, Shape* shape, - JSObject* prototypeObj, uint32_t slot) - : ICStub(InstanceOf_Function, stubCode), - shape_(shape), - prototypeObj_(prototypeObj), - slot_(slot) -{ } - ICCall_Scripted::ICCall_Scripted(JitCode* stubCode, ICStub* firstMonitorStub, JSFunction* callee, JSObject* templateObject, uint32_t pcOffset) diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h index 4bc15d1a21a7..4f434325edb5 100644 --- a/js/src/jit/BaselineIC.h +++ b/js/src/jit/BaselineIC.h @@ -1454,7 +1454,6 @@ class ICInstanceOf_Fallback : public ICFallbackStub static const uint16_t UNOPTIMIZABLE_ACCESS_BIT = 0x1; public: - static const uint32_t MAX_OPTIMIZED_STUBS = 4; void noteUnoptimizableAccess() { extra_ |= UNOPTIMIZABLE_ACCESS_BIT; @@ -1478,59 +1477,6 @@ class ICInstanceOf_Fallback : public ICFallbackStub }; }; -class ICInstanceOf_Function : public ICStub -{ - friend class ICStubSpace; - - GCPtrShape shape_; - GCPtrObject prototypeObj_; - uint32_t slot_; - - ICInstanceOf_Function(JitCode* stubCode, Shape* shape, JSObject* prototypeObj, uint32_t slot); - - public: - GCPtrShape& shape() { - return shape_; - } - GCPtrObject& prototypeObject() { - return prototypeObj_; - } - uint32_t slot() const { - return slot_; - } - static size_t offsetOfShape() { - return offsetof(ICInstanceOf_Function, shape_); - } - static size_t offsetOfPrototypeObject() { - return offsetof(ICInstanceOf_Function, prototypeObj_); - } - static size_t offsetOfSlot() { - return offsetof(ICInstanceOf_Function, slot_); - } - - class Compiler : public ICStubCompiler { - RootedShape shape_; - RootedObject prototypeObj_; - uint32_t slot_; - - protected: - MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override; - - public: - Compiler(JSContext* cx, Shape* shape, JSObject* prototypeObj, uint32_t slot) - : ICStubCompiler(cx, ICStub::InstanceOf_Function, Engine::Baseline), - shape_(cx, shape), - prototypeObj_(cx, prototypeObj), - slot_(slot) - {} - - ICStub* getStub(ICStubSpace* space) override { - return newStub(space, getStubCode(), shape_, prototypeObj_, - slot_); - } - }; -}; - // TypeOf // JSOP_TYPEOF // JSOP_TYPEOFEXPR diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp index a439b48433f7..ecb1cc213d93 100644 --- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -1383,27 +1383,49 @@ BaselineInspector::instanceOfData(jsbytecode* pc, Shape** shape, uint32_t* slot, JSObject** prototypeObject) { MOZ_ASSERT(*pc == JSOP_INSTANCEOF); - if (!hasBaselineScript()) return false; const ICEntry& entry = icEntryFromPC(pc); + ICStub* firstStub = entry.firstStub(); - ICStub* stub = entry.firstStub(); - if (!stub->isInstanceOf_Function() || - !stub->next()->isInstanceOf_Fallback() || - stub->next()->toInstanceOf_Fallback()->hadUnoptimizableAccess()) - { + // Ensure singleton instanceof stub + if (!firstStub->next() || + !firstStub->isCacheIR_Regular() || + !firstStub->next()->isInstanceOf_Fallback() || + firstStub->next()->toInstanceOf_Fallback()->hadUnoptimizableAccess()) + { + return false; + } + + ICCacheIR_Regular* stub = entry.firstStub()->toCacheIR_Regular(); + CacheIRReader reader(stub->stubInfo()); + + ObjOperandId rhsId = ObjOperandId(1); + ObjOperandId resId = ObjOperandId(2); + + if (!reader.matchOp(CacheOp::GuardIsObject, rhsId)) return false; - } - ICInstanceOf_Function* optStub = stub->toInstanceOf_Function(); - *shape = optStub->shape(); - *prototypeObject = optStub->prototypeObject(); - *slot = optStub->slot(); + if (!reader.matchOp(CacheOp::GuardShape, rhsId)) + return false; + + *shape = stub->stubInfo()->getStubField(stub, reader.stubOffset()); + + if (!reader.matchOp(CacheOp::LoadObject, resId)) + return false; + + *prototypeObject = stub->stubInfo()->getStubField(stub, reader.stubOffset()).get(); if (IsInsideNursery(*prototypeObject)) return false; + if (!reader.matchOp(CacheOp::GuardFunctionPrototype, rhsId)) + return false; + + reader.skip(); // Skip over the protoID; + + *slot = stub->stubInfo()->getStubRawWord(stub, reader.stubOffset()); + return true; } diff --git a/js/src/jit/CacheIR.cpp b/js/src/jit/CacheIR.cpp index a4fcf191ec22..8ca6bd7a02b3 100644 --- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -4001,6 +4001,113 @@ SetPropIRGenerator::tryAttachAddSlotStub(HandleObjectGroup oldGroup, HandleShape return true; } +InstanceOfIRGenerator::InstanceOfIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, + ICState::Mode mode, HandleValue lhs, HandleValue rhs) + : IRGenerator(cx, script, pc, CacheKind::InstanceOf, mode), + lhsVal_(lhs), + rhsVal_(rhs) +{ } + +bool +InstanceOfIRGenerator::tryAttachStub() +{ + MOZ_ASSERT(cacheKind_ == CacheKind::InstanceOf); + AutoAssertNoPendingException aanpe(cx_); + RootedFunction fun(cx_, &rhsVal_.toObject().as()); + + if (fun->isBoundFunction()) { + trackNotAttached(); + return false; + } + + // If the user has supplied their own @@hasInstance method we shouldn't + // clobber it. + if (!js::FunctionHasDefaultHasInstance(fun, cx_->wellKnownSymbols())) { + trackNotAttached(); + return false; + } + + // Refuse to optimize any function whose [[Prototype]] isn't + // Function.prototype. + if (!fun->hasStaticPrototype() || fun->hasUncacheableProto()) { + trackNotAttached(); + return false; + } + + Value funProto = cx_->global()->getPrototype(JSProto_Function); + if (!funProto.isObject() || fun->staticPrototype() != &funProto.toObject()) { + trackNotAttached(); + return false; + } + + // Ensure that the function's prototype slot is the same. + Shape* shape = fun->lookupPure(cx_->names().prototype); + if (!shape || !shape->isDataProperty()) { + trackNotAttached(); + return false; + } + + uint32_t slot = shape->slot(); + + MOZ_ASSERT(fun->numFixedSlots() == 0, "Stub code relies on this"); + if (!fun->getSlot(slot).isObject()) { + trackNotAttached(); + return false; + } + + JSObject* prototypeObject = &fun->getSlot(slot).toObject(); + + // Abstract Objects + ValOperandId lhs(writer.setInputOperandId(0)); + ValOperandId rhs(writer.setInputOperandId(1)); + + ObjOperandId rhsId = writer.guardIsObject(rhs); + writer.guardShape(rhsId, fun->lastProperty()); + + // Load prototypeObject into the cache -- consumed twice in the IC + ObjOperandId protoId = writer.loadObject(prototypeObject); + // Ensure that rhs[slot] == prototypeObject. + writer.guardFunctionPrototype(rhsId, slot, protoId); + + // Needn't guard LHS is object, because the actual stub can handle that + // and correctly return false. + writer.loadInstanceOfObjectResult(lhs, protoId, slot); + writer.returnFromIC(); + trackAttached("InstanceOf"); + return true; +} + +void +InstanceOfIRGenerator::trackAttached(const char* name) +{ +#ifdef JS_CACHEIR_SPEW + CacheIRSpewer& sp = CacheIRSpewer::singleton(); + if (sp.enabled()) { + LockGuard guard(sp.lock()); + sp.beginCache(guard, *this); + sp.valueProperty(guard, "lhs", lhsVal_); + sp.valueProperty(guard, "rhs", rhsVal_); + sp.attached(guard, name); + sp.endCache(guard); + } +#endif +} + +void +InstanceOfIRGenerator::trackNotAttached() +{ +#ifdef JS_CACHEIR_SPEW + CacheIRSpewer& sp = CacheIRSpewer::singleton(); + if (sp.enabled()) { + LockGuard guard(sp.lock()); + sp.beginCache(guard, *this); + sp.valueProperty(guard, "lhs", lhsVal_); + sp.valueProperty(guard, "rhs", rhsVal_); + sp.endCache(guard); + } +#endif +} + TypeOfIRGenerator::TypeOfIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, ICState::Mode mode, HandleValue value) : IRGenerator(cx, script, pc, CacheKind::TypeOf, mode), diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h index 62e7ead7f922..e02d95f4a2a9 100644 --- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -148,6 +148,7 @@ class TypedOperandId : public OperandId _(In) \ _(HasOwn) \ _(TypeOf) \ + _(InstanceOf) \ _(GetIterator) \ _(Compare) \ _(Call) @@ -195,6 +196,7 @@ extern const char* CacheKindNames[]; _(GuardGroupHasUnanalyzedNewScript) \ _(GuardIndexIsNonNegative) \ _(GuardXrayExpandoShapeAndDefaultProto) \ + _(GuardFunctionPrototype) \ _(LoadStackValue) \ _(LoadObject) \ _(LoadProto) \ @@ -264,6 +266,7 @@ extern const char* CacheKindNames[]; _(LoadUndefinedResult) \ _(LoadBooleanResult) \ _(LoadStringResult) \ + _(LoadInstanceOfObjectResult) \ _(LoadTypeOfObjectResult) \ \ _(CallStringSplitResult) \ @@ -533,6 +536,12 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter writeOpWithOperandId(CacheOp::GuardXrayExpandoShapeAndDefaultProto, obj); buffer_.writeByte(uint32_t(!!shapeWrapper)); addStubField(uintptr_t(shapeWrapper), StubField::Type::JSObject); } + // Guard rhs[slot] == prototypeObject + void guardFunctionPrototype(ObjOperandId rhs, uint32_t slot, ObjOperandId protoId) { + writeOpWithOperandId(CacheOp::GuardFunctionPrototype, rhs); + writeOperandId(protoId); + addStubField(slot, StubField::Type::RawWord); + } void guardGroup(ObjOperandId obj, ObjectGroup* group) { writeOpWithOperandId(CacheOp::GuardGroup, obj); addStubField(uintptr_t(group), StubField::Type::ObjectGroup); @@ -992,10 +1001,14 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter void loadObjectResult(ObjOperandId obj) { writeOpWithOperandId(CacheOp::LoadObjectResult, obj); } + void loadInstanceOfObjectResult(ValOperandId lhs, ObjOperandId protoId, uint32_t slot) { + writeOp(CacheOp::LoadInstanceOfObjectResult); + writeOperandId(lhs); + writeOperandId(protoId); + } void loadTypeOfObjectResult(ObjOperandId obj) { writeOpWithOperandId(CacheOp::LoadTypeOfObjectResult, obj); } - void callStringSplitResult(StringOperandId str, StringOperandId sep, ObjectGroup* group) { writeOp(CacheOp::CallStringSplitResult); writeOperandId(str); @@ -1063,6 +1076,9 @@ class MOZ_RAII CacheIRReader return CacheOp(buffer_.readByte()); } + // Skip data not currently used. + void skip() { buffer_.readByte(); } + ValOperandId valOperandId() { return ValOperandId(buffer_.readByte()); } ObjOperandId objOperandId() { return ObjOperandId(buffer_.readByte()); } StringOperandId stringOperandId() { return StringOperandId(buffer_.readByte()); } @@ -1485,6 +1501,20 @@ class MOZ_RAII HasPropIRGenerator : public IRGenerator bool tryAttachStub(); }; +class MOZ_RAII InstanceOfIRGenerator : public IRGenerator +{ + HandleValue lhsVal_; + HandleValue rhsVal_; + + void trackAttached(const char* name); + void trackNotAttached(); + public: + InstanceOfIRGenerator(JSContext*, HandleScript, jsbytecode*, ICState::Mode, + HandleValue, HandleValue); + + bool tryAttachStub(); +}; + class MOZ_RAII TypeOfIRGenerator : public IRGenerator { HandleValue val_; diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp index 6f9fd754d826..f76dbcbe164b 100644 --- a/js/src/jit/CacheIRCompiler.cpp +++ b/js/src/jit/CacheIRCompiler.cpp @@ -863,6 +863,13 @@ AsGCPtr(uintptr_t* ptr) return reinterpret_cast*>(ptr); } +uintptr_t +CacheIRStubInfo::getStubRawWord(ICStub* stub, uint32_t offset) const { + uint8_t* stubData = (uint8_t*)stub + stubDataOffset_; + MOZ_ASSERT(uintptr_t(stubData) % sizeof(uintptr_t) == 0); + return *(uintptr_t*)(stubData + offset); +} + template GCPtr& CacheIRStubInfo::getStubField(Stub* stub, uint32_t offset) const @@ -2631,3 +2638,50 @@ CacheIRCompiler::emitCallObjectHasSparseElementResult() masm.adjustStack(sizeof(Value)); return true; } + +bool +CacheIRCompiler::emitLoadInstanceOfObjectResult() +{ + AutoOutputRegister output(*this); + ValueOperand lhs = allocator.useValueRegister(masm, reader.valOperandId()); + Register proto = allocator.useRegister(masm, reader.objOperandId()); + + AutoScratchRegisterMaybeOutput scratch(allocator, masm, output); + + FailurePath* failure; + if (!addFailurePath(&failure)) + return false; + + Label returnFalse, returnTrue, done; + masm.branchTestObject(Assembler::NotEqual, lhs, &returnFalse); + + // LHS is an object. Load its proto. + masm.unboxObject(lhs, scratch); + masm.loadObjProto(scratch, scratch); + { + // Walk the proto chain until we either reach the target object, + // nullptr or LazyProto. + Label loop; + masm.bind(&loop); + + masm.branchPtr(Assembler::Equal, scratch, proto, &returnTrue); + masm.branchTestPtr(Assembler::Zero, scratch, scratch, &returnFalse); + + MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1); + masm.branchPtr(Assembler::Equal, scratch, ImmWord(1), failure->label()); + + masm.loadObjProto(scratch, scratch); + masm.jump(&loop); + } + + + masm.bind(&returnFalse); + EmitStoreBoolean(masm, false, output); + masm.jump(&done); + + masm.bind(&returnTrue); + EmitStoreBoolean(masm, true, output); + //fallthrough + masm.bind(&done); + return true; +} \ No newline at end of file diff --git a/js/src/jit/CacheIRCompiler.h b/js/src/jit/CacheIRCompiler.h index 79620e3529f8..a1d7755a5fe6 100644 --- a/js/src/jit/CacheIRCompiler.h +++ b/js/src/jit/CacheIRCompiler.h @@ -47,6 +47,7 @@ namespace jit { _(LoadStringLengthResult) \ _(LoadStringCharResult) \ _(LoadArgumentsObjectArgResult) \ + _(LoadInstanceOfObjectResult) \ _(LoadDenseElementResult) \ _(LoadDenseElementHoleResult) \ _(LoadDenseElementExistsResult) \ @@ -712,6 +713,8 @@ class CacheIRStubInfo return getStubField(stub, field); } + uintptr_t getStubRawWord(ICStub* stub, uint32_t field) const; + void copyStubData(ICStub* src, ICStub* dest) const; }; diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 38821694d517..3a6b65372bde 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -334,6 +334,7 @@ CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool) case CacheKind::Call: case CacheKind::Compare: case CacheKind::TypeOf: + case CacheKind::InstanceOf: MOZ_CRASH("Unsupported IC"); } MOZ_CRASH(); diff --git a/js/src/jit/IonCacheIRCompiler.cpp b/js/src/jit/IonCacheIRCompiler.cpp index 83c5a9274fc3..df3936cd67ca 100644 --- a/js/src/jit/IonCacheIRCompiler.cpp +++ b/js/src/jit/IonCacheIRCompiler.cpp @@ -533,6 +533,7 @@ IonCacheIRCompiler::init() case CacheKind::Call: case CacheKind::Compare: case CacheKind::TypeOf: + case CacheKind::InstanceOf: MOZ_CRASH("Unsupported IC"); } @@ -844,6 +845,35 @@ IonCacheIRCompiler::emitGuardXrayExpandoShapeAndDefaultProto() return true; } +bool +IonCacheIRCompiler::emitGuardFunctionPrototype() +{ + Register obj = allocator.useRegister(masm, reader.objOperandId()); + Register prototypeObject = allocator.useRegister(masm, reader.objOperandId()); + + // Allocate registers before the failure path to make sure they're registered + // by addFailurePath. + AutoScratchRegister scratch1(allocator, masm); + AutoScratchRegister scratch2(allocator, masm); + + FailurePath* failure; + if (!addFailurePath(&failure)) + return false; + + // Guard on the .prototype object. + masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), scratch1); + uintptr_t slot = readStubWord(reader.stubOffset(), StubField::Type::RawWord); + masm.move32(Imm32(slot), scratch2); + BaseValueIndex prototypeSlot(scratch1, scratch2); + masm.branchTestObject(Assembler::NotEqual, prototypeSlot, failure->label()); + masm.unboxObject(prototypeSlot, scratch1); + masm.branchPtr(Assembler::NotEqual, + prototypeObject, + scratch1, failure->label()); + + return true; +} + bool IonCacheIRCompiler::emitLoadFixedSlotResult() { diff --git a/js/src/jit/IonIC.cpp b/js/src/jit/IonIC.cpp index 3e76fb43a253..5d65f47cd12d 100644 --- a/js/src/jit/IonIC.cpp +++ b/js/src/jit/IonIC.cpp @@ -61,6 +61,7 @@ IonIC::scratchRegisterForEntryJump() case CacheKind::Call: case CacheKind::Compare: case CacheKind::TypeOf: + case CacheKind::InstanceOf: MOZ_CRASH("Unsupported IC"); } diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp index d322292fa547..8bc48b278223 100644 --- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -297,12 +297,6 @@ ICStub::trace(JSTracer* trc) TraceEdge(trc, &constantStub->value(), "baseline-getintrinsic-constant-value"); break; } - case ICStub::InstanceOf_Function: { - ICInstanceOf_Function* instanceofStub = toInstanceOf_Function(); - TraceEdge(trc, &instanceofStub->shape(), "baseline-instanceof-fun-shape"); - TraceEdge(trc, &instanceofStub->prototypeObject(), "baseline-instanceof-fun-prototype"); - break; - } case ICStub::NewArray_Fallback: { ICNewArray_Fallback* stub = toNewArray_Fallback(); TraceNullableEdge(trc, &stub->templateObject(), "baseline-newarray-template");