|
@@ -0,0 +1,1048 @@
|
|
|
+# HG changeset patch
|
|
|
+# User Matthew Gaudet <mgaudet@mozilla.com>
|
|
|
+# Date 1513117305 21600
|
|
|
+# Node ID 5d6cc408dfd9228ad0529ba718b5e8a2a4889df1
|
|
|
+# Parent df48514c0f80ce5be2a8c37ca0935d481838242c
|
|
|
+Bug 1420910: Convert the Baseline InstanceOf IC to CacheIR r=jandem
|
|
|
+
|
|
|
+This is the preliminary patch to convert the Baseline InstanceOf
|
|
|
+IC to CacheIR, which will later allow us to add support to IonMonkey
|
|
|
+
|
|
|
+diff --git a/js/src/jit-test/tests/cacheir/bug1420910.js b/js/src/jit-test/tests/cacheir/bug1420910.js
|
|
|
+new file mode 100644
|
|
|
+--- /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
|
|
|
+--- a/js/src/jit/BaselineCacheIRCompiler.cpp
|
|
|
++++ b/js/src/jit/BaselineCacheIRCompiler.cpp
|
|
|
+@@ -452,16 +452,44 @@ BaselineCacheIRCompiler::emitGuardXrayEx
|
|
|
+ masm.branchTestObject(Assembler::Equal, expandoAddress, failure->label());
|
|
|
+ masm.bind(&done);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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()
|
|
|
+ {
|
|
|
+ AutoOutputRegister output(*this);
|
|
|
+ Register obj = allocator.useRegister(masm, reader.objOperandId());
|
|
|
+ AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
|
|
|
+
|
|
|
+ masm.load32(stubAddress(reader.stubOffset()), scratch);
|
|
|
+ masm.loadValue(BaseIndex(obj, scratch, TimesOne), output.valueReg());
|
|
|
+@@ -2078,16 +2106,17 @@ BaselineCacheIRCompiler::init(CacheKind
|
|
|
+ allocator.initInputLocation(0, R0);
|
|
|
+ break;
|
|
|
+ case CacheKind::Compare:
|
|
|
+ case CacheKind::GetElem:
|
|
|
+ case CacheKind::GetPropSuper:
|
|
|
+ case CacheKind::SetProp:
|
|
|
+ case CacheKind::In:
|
|
|
+ case CacheKind::HasOwn:
|
|
|
++ case CacheKind::InstanceOf:
|
|
|
+ MOZ_ASSERT(numInputs == 2);
|
|
|
+ allocator.initInputLocation(0, R0);
|
|
|
+ allocator.initInputLocation(1, R1);
|
|
|
+ break;
|
|
|
+ case CacheKind::GetElemSuper:
|
|
|
+ MOZ_ASSERT(numInputs == 3);
|
|
|
+ allocator.initInputLocation(0, BaselineFrameSlot(0));
|
|
|
+ allocator.initInputLocation(1, R0);
|
|
|
+diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp
|
|
|
+--- a/js/src/jit/BaselineIC.cpp
|
|
|
++++ b/js/src/jit/BaselineIC.cpp
|
|
|
+@@ -4287,56 +4287,44 @@ ICIteratorClose_Fallback::Compiler::gene
|
|
|
+ }
|
|
|
+
|
|
|
+ //
|
|
|
+ // InstanceOf_Fallback
|
|
|
+ //
|
|
|
+
|
|
|
+ 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;
|
|
|
+-
|
|
|
+- // If the user has supplied their own @@hasInstance method we shouldn't
|
|
|
+- // clobber it.
|
|
|
+- if (!js::FunctionHasDefaultHasInstance(fun, cx->wellKnownSymbols()))
|
|
|
+- return true;
|
|
|
+-
|
|
|
+- // Refuse to optimize any function whose [[Prototype]] isn't
|
|
|
+- // Function.prototype.
|
|
|
+- if (!fun->hasStaticPrototype() || fun->hasUncacheableProto())
|
|
|
+- return true;
|
|
|
+-
|
|
|
+- Value funProto = cx->global()->getPrototype(JSProto_Function);
|
|
|
+- if (funProto.isObject() && fun->staticPrototype() != &funProto.toObject())
|
|
|
+- return true;
|
|
|
+-
|
|
|
+- Shape* shape = fun->lookupPure(cx->names().prototype);
|
|
|
+- if (!shape || !shape->isDataProperty())
|
|
|
+- return true;
|
|
|
+-
|
|
|
+- 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;
|
|
|
++ FallbackICSpew(cx, stub, "InstanceOf");
|
|
|
++
|
|
|
++ if (stub->state().maybeTransition())
|
|
|
++ stub->discardStubs(cx);
|
|
|
++
|
|
|
++ if (stub->state().canAttachStub()) {
|
|
|
++ RootedScript script(cx, frame->script());
|
|
|
++ jsbytecode* pc = stub->icEntry()->pc(script);
|
|
|
++
|
|
|
++ ICStubEngine engine = ICStubEngine::Baseline;
|
|
|
++ InstanceOfIRGenerator gen(cx, script, pc, stub->state().mode(),
|
|
|
++ lhs,
|
|
|
++ rhs);
|
|
|
++
|
|
|
++ 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();
|
|
|
++ }
|
|
|
++
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ static bool
|
|
|
+ DoInstanceOfFallback(JSContext* cx, BaselineFrame* frame, ICInstanceOf_Fallback* stub,
|
|
|
+ HandleValue lhs, HandleValue rhs, MutableHandleValue res)
|
|
|
+ {
|
|
|
+ FallbackICSpew(cx, stub, "InstanceOf");
|
|
|
+@@ -4357,22 +4345,18 @@ DoInstanceOfFallback(JSContext* cx, Base
|
|
|
+ stub->noteUnoptimizableAccess();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // For functions, keep track of the |prototype| property in type information,
|
|
|
+ // 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<JSFunction>());
|
|
|
+ bool attached = false;
|
|
|
+- if (!TryAttachInstanceOfStub(cx, frame, stub, fun, &attached))
|
|
|
++ if (!TryAttachInstanceOfStub(cx, frame, stub, lhs, rhs, &attached))
|
|
|
+ return false;
|
|
|
+ if (!attached)
|
|
|
+ stub->noteUnoptimizableAccess();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ typedef bool (*DoInstanceOfFallbackFn)(JSContext*, BaselineFrame*, ICInstanceOf_Fallback*,
|
|
|
+ HandleValue, HandleValue, MutableHandleValue);
|
|
|
+@@ -4394,92 +4378,16 @@ ICInstanceOf_Fallback::Compiler::generat
|
|
|
+ masm.pushValue(R1);
|
|
|
+ masm.pushValue(R0);
|
|
|
+ masm.push(ICStubReg);
|
|
|
+ pushStubPayload(masm, R0.scratchReg());
|
|
|
+
|
|
|
+ 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
|
|
|
+ //
|
|
|
+
|
|
|
+ static bool
|
|
|
+ DoTypeOfFallback(JSContext* cx, BaselineFrame* frame, ICTypeOf_Fallback* stub, HandleValue val,
|
|
|
+ MutableHandleValue res)
|
|
|
+ {
|
|
|
+@@ -4662,24 +4570,16 @@ ICTypeUpdate_ObjectGroup::ICTypeUpdate_O
|
|
|
+ ICGetIntrinsic_Constant::ICGetIntrinsic_Constant(JitCode* stubCode, const Value& value)
|
|
|
+ : ICStub(GetIntrinsic_Constant, stubCode),
|
|
|
+ value_(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)
|
|
|
+ : ICMonitoredStub(ICStub::Call_Scripted, stubCode, firstMonitorStub),
|
|
|
+ callee_(callee),
|
|
|
+ templateObject_(templateObject),
|
|
|
+ pcOffset_(pcOffset)
|
|
|
+ { }
|
|
|
+diff --git a/js/src/jit/BaselineIC.h b/js/src/jit/BaselineIC.h
|
|
|
+--- a/js/src/jit/BaselineIC.h
|
|
|
++++ b/js/src/jit/BaselineIC.h
|
|
|
+@@ -1449,17 +1449,16 @@ class ICInstanceOf_Fallback : public ICF
|
|
|
+
|
|
|
+ explicit ICInstanceOf_Fallback(JitCode* stubCode)
|
|
|
+ : ICFallbackStub(ICStub::InstanceOf_Fallback, stubCode)
|
|
|
+ { }
|
|
|
+
|
|
|
+ static const uint16_t UNOPTIMIZABLE_ACCESS_BIT = 0x1;
|
|
|
+
|
|
|
+ public:
|
|
|
+- static const uint32_t MAX_OPTIMIZED_STUBS = 4;
|
|
|
+
|
|
|
+ void noteUnoptimizableAccess() {
|
|
|
+ extra_ |= UNOPTIMIZABLE_ACCESS_BIT;
|
|
|
+ }
|
|
|
+ bool hadUnoptimizableAccess() const {
|
|
|
+ return extra_ & UNOPTIMIZABLE_ACCESS_BIT;
|
|
|
+ }
|
|
|
+
|
|
|
+@@ -1473,69 +1472,16 @@ class ICInstanceOf_Fallback : public ICF
|
|
|
+ { }
|
|
|
+
|
|
|
+ ICStub* getStub(ICStubSpace* space) override {
|
|
|
+ return newStub<ICInstanceOf_Fallback>(space, getStubCode());
|
|
|
+ }
|
|
|
+ };
|
|
|
+ };
|
|
|
+
|
|
|
+-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<ICInstanceOf_Function>(space, getStubCode(), shape_, prototypeObj_,
|
|
|
+- slot_);
|
|
|
+- }
|
|
|
+- };
|
|
|
+-};
|
|
|
+-
|
|
|
+ // TypeOf
|
|
|
+ // JSOP_TYPEOF
|
|
|
+ // JSOP_TYPEOFEXPR
|
|
|
+ class ICTypeOf_Fallback : public ICFallbackStub
|
|
|
+ {
|
|
|
+ friend class ICStubSpace;
|
|
|
+
|
|
|
+ explicit ICTypeOf_Fallback(JitCode* stubCode)
|
|
|
+diff --git a/js/src/jit/BaselineInspector.cpp b/js/src/jit/BaselineInspector.cpp
|
|
|
+--- a/js/src/jit/BaselineInspector.cpp
|
|
|
++++ b/js/src/jit/BaselineInspector.cpp
|
|
|
+@@ -1378,32 +1378,54 @@ BaselineInspector::expectedPropertyAcces
|
|
|
+ return (type == MIRType::None) ? MIRType::Value : type;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool
|
|
|
+ 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;
|
|
|
+- }
|
|
|
++
|
|
|
++ if (!reader.matchOp(CacheOp::GuardShape, rhsId))
|
|
|
++ return false;
|
|
|
+
|
|
|
+- ICInstanceOf_Function* optStub = stub->toInstanceOf_Function();
|
|
|
+- *shape = optStub->shape();
|
|
|
+- *prototypeObject = optStub->prototypeObject();
|
|
|
+- *slot = optStub->slot();
|
|
|
++ *shape = stub->stubInfo()->getStubField<Shape*>(stub, reader.stubOffset());
|
|
|
++
|
|
|
++ if (!reader.matchOp(CacheOp::LoadObject, resId))
|
|
|
++ return false;
|
|
|
++
|
|
|
++ *prototypeObject = stub->stubInfo()->getStubField<JSObject*>(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
|
|
|
+--- a/js/src/jit/CacheIR.cpp
|
|
|
++++ b/js/src/jit/CacheIR.cpp
|
|
|
+@@ -4201,16 +4201,123 @@ SetPropIRGenerator::tryAttachAddSlotStub
|
|
|
+ }
|
|
|
+ }
|
|
|
+ writer.returnFromIC();
|
|
|
+
|
|
|
+ typeCheckInfo_.set(oldGroup, id);
|
|
|
+ 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<JSFunction>());
|
|
|
++
|
|
|
++ 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<Mutex> 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<Mutex> 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),
|
|
|
+ val_(value)
|
|
|
+ { }
|
|
|
+
|
|
|
+ bool
|
|
|
+ TypeOfIRGenerator::tryAttachStub()
|
|
|
+diff --git a/js/src/jit/CacheIR.h b/js/src/jit/CacheIR.h
|
|
|
+--- a/js/src/jit/CacheIR.h
|
|
|
++++ b/js/src/jit/CacheIR.h
|
|
|
+@@ -143,16 +143,17 @@ class TypedOperandId : public OperandId
|
|
|
+ _(GetPropSuper) \
|
|
|
+ _(GetElemSuper) \
|
|
|
+ _(SetProp) \
|
|
|
+ _(SetElem) \
|
|
|
+ _(BindName) \
|
|
|
+ _(In) \
|
|
|
+ _(HasOwn) \
|
|
|
+ _(TypeOf) \
|
|
|
++ _(InstanceOf) \
|
|
|
+ _(GetIterator) \
|
|
|
+ _(Compare) \
|
|
|
+ _(Call)
|
|
|
+
|
|
|
+ enum class CacheKind : uint8_t
|
|
|
+ {
|
|
|
+ #define DEFINE_KIND(kind) kind,
|
|
|
+ CACHE_IR_KINDS(DEFINE_KIND)
|
|
|
+@@ -190,16 +191,17 @@ extern const char* CacheKindNames[];
|
|
|
+ _(GuardNoUnboxedExpando) \
|
|
|
+ _(GuardAndLoadUnboxedExpando) \
|
|
|
+ _(GuardAndGetIndexFromString) \
|
|
|
+ _(GuardAndGetIterator) \
|
|
|
+ _(GuardHasGetterSetter) \
|
|
|
+ _(GuardGroupHasUnanalyzedNewScript) \
|
|
|
+ _(GuardIndexIsNonNegative) \
|
|
|
+ _(GuardXrayExpandoShapeAndDefaultProto) \
|
|
|
++ _(GuardFunctionPrototype) \
|
|
|
+ _(LoadStackValue) \
|
|
|
+ _(LoadObject) \
|
|
|
+ _(LoadProto) \
|
|
|
+ _(LoadEnclosingEnvironment) \
|
|
|
+ _(LoadWrapperTarget) \
|
|
|
+ \
|
|
|
+ _(MegamorphicLoadSlotResult) \
|
|
|
+ _(MegamorphicLoadSlotByValueResult) \
|
|
|
+@@ -259,16 +261,17 @@ extern const char* CacheKindNames[];
|
|
|
+ _(CallNativeGetterResult) \
|
|
|
+ _(CallProxyGetResult) \
|
|
|
+ _(CallProxyGetByValueResult) \
|
|
|
+ _(CallProxyHasPropResult) \
|
|
|
+ _(CallObjectHasSparseElementResult) \
|
|
|
+ _(LoadUndefinedResult) \
|
|
|
+ _(LoadBooleanResult) \
|
|
|
+ _(LoadStringResult) \
|
|
|
++ _(LoadInstanceOfObjectResult) \
|
|
|
+ _(LoadTypeOfObjectResult) \
|
|
|
+ \
|
|
|
+ _(CallStringSplitResult) \
|
|
|
+ \
|
|
|
+ _(CompareStringResult) \
|
|
|
+ _(CompareObjectResult) \
|
|
|
+ _(CompareSymbolResult) \
|
|
|
+ \
|
|
|
+@@ -546,16 +549,22 @@ class MOZ_RAII CacheIRWriter : public JS
|
|
|
+ guardShape(obj, shape);
|
|
|
+ }
|
|
|
+ void guardXrayExpandoShapeAndDefaultProto(ObjOperandId obj, JSObject* shapeWrapper) {
|
|
|
+ assertSameCompartment(shapeWrapper);
|
|
|
+ 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);
|
|
|
++ }
|
|
|
+ private:
|
|
|
+ // Use (or create) a specialization below to clarify what constaint the
|
|
|
+ // group guard is implying.
|
|
|
+ void guardGroup(ObjOperandId obj, ObjectGroup* group) {
|
|
|
+ writeOpWithOperandId(CacheOp::GuardGroup, obj);
|
|
|
+ addStubField(uintptr_t(group), StubField::Type::ObjectGroup);
|
|
|
+ }
|
|
|
+ public:
|
|
|
+@@ -1031,20 +1040,24 @@ class MOZ_RAII CacheIRWriter : public JS
|
|
|
+ }
|
|
|
+ void loadEnvironmentDynamicSlotResult(ObjOperandId obj, size_t offset) {
|
|
|
+ writeOpWithOperandId(CacheOp::LoadEnvironmentDynamicSlotResult, obj);
|
|
|
+ addStubField(offset, StubField::Type::RawWord);
|
|
|
+ }
|
|
|
+ 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);
|
|
|
+ writeOperandId(sep);
|
|
|
+ addStubField(uintptr_t(group), StubField::Type::ObjectGroup);
|
|
|
+ }
|
|
|
+
|
|
|
+ void compareStringResult(uint32_t op, StringOperandId lhs, StringOperandId rhs) {
|
|
|
+@@ -1102,16 +1115,19 @@ class MOZ_RAII CacheIRReader
|
|
|
+ explicit CacheIRReader(const CacheIRStubInfo* stubInfo);
|
|
|
+
|
|
|
+ bool more() const { return buffer_.more(); }
|
|
|
+
|
|
|
+ CacheOp readOp() {
|
|
|
+ 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()); }
|
|
|
+ SymbolOperandId symbolOperandId() { return SymbolOperandId(buffer_.readByte()); }
|
|
|
+ Int32OperandId int32OperandId() { return Int32OperandId(buffer_.readByte()); }
|
|
|
+
|
|
|
+ uint32_t stubOffset() { return buffer_.readByte() * sizeof(uintptr_t); }
|
|
|
+ GuardClassKind guardClassKind() { return GuardClassKind(buffer_.readByte()); }
|
|
|
+@@ -1524,16 +1540,30 @@ class MOZ_RAII HasPropIRGenerator : publ
|
|
|
+ public:
|
|
|
+ // NOTE: Argument order is PROPERTY, OBJECT
|
|
|
+ HasPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
|
|
|
+ ICState::Mode mode, HandleValue idVal, HandleValue val);
|
|
|
+
|
|
|
+ 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_;
|
|
|
+
|
|
|
+ bool tryAttachPrimitive(ValOperandId valId);
|
|
|
+ bool tryAttachObject(ValOperandId valId);
|
|
|
+
|
|
|
+ public:
|
|
|
+diff --git a/js/src/jit/CacheIRCompiler.cpp b/js/src/jit/CacheIRCompiler.cpp
|
|
|
+--- a/js/src/jit/CacheIRCompiler.cpp
|
|
|
++++ b/js/src/jit/CacheIRCompiler.cpp
|
|
|
+@@ -858,16 +858,23 @@ CacheIRStubInfo::copyStubData(ICStub* sr
|
|
|
+
|
|
|
+ template <typename T>
|
|
|
+ static GCPtr<T>*
|
|
|
+ AsGCPtr(uintptr_t* ptr)
|
|
|
+ {
|
|
|
+ return reinterpret_cast<GCPtr<T>*>(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<class Stub, class T>
|
|
|
+ GCPtr<T>&
|
|
|
+ CacheIRStubInfo::getStubField(Stub* stub, uint32_t offset) const
|
|
|
+ {
|
|
|
+ uint8_t* stubData = (uint8_t*)stub + stubDataOffset_;
|
|
|
+ MOZ_ASSERT(uintptr_t(stubData) % sizeof(uintptr_t) == 0);
|
|
|
+
|
|
|
+ return *AsGCPtr<T>((uintptr_t*)(stubData + offset));
|
|
|
+@@ -2626,8 +2633,55 @@ CacheIRCompiler::emitCallObjectHasSparse
|
|
|
+ masm.jump(failure->label());
|
|
|
+
|
|
|
+ masm.bind(&ok);
|
|
|
+ masm.setFramePushed(framePushed);
|
|
|
+ masm.loadTypedOrValue(Address(masm.getStackPointer(), 0), output);
|
|
|
+ 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
|
|
|
+--- a/js/src/jit/CacheIRCompiler.h
|
|
|
++++ b/js/src/jit/CacheIRCompiler.h
|
|
|
+@@ -42,16 +42,17 @@ namespace jit {
|
|
|
+ _(LoadUndefinedResult) \
|
|
|
+ _(LoadBooleanResult) \
|
|
|
+ _(LoadInt32ArrayLengthResult) \
|
|
|
+ _(LoadArgumentsObjectLengthResult) \
|
|
|
+ _(LoadFunctionLengthResult) \
|
|
|
+ _(LoadStringLengthResult) \
|
|
|
+ _(LoadStringCharResult) \
|
|
|
+ _(LoadArgumentsObjectArgResult) \
|
|
|
++ _(LoadInstanceOfObjectResult) \
|
|
|
+ _(LoadDenseElementResult) \
|
|
|
+ _(LoadDenseElementHoleResult) \
|
|
|
+ _(LoadDenseElementExistsResult) \
|
|
|
+ _(LoadDenseElementHoleExistsResult) \
|
|
|
+ _(LoadTypedElementExistsResult) \
|
|
|
+ _(LoadTypedElementResult) \
|
|
|
+ _(LoadObjectResult) \
|
|
|
+ _(LoadTypeOfObjectResult) \
|
|
|
+@@ -707,16 +708,18 @@ class CacheIRStubInfo
|
|
|
+ template <class Stub, class T>
|
|
|
+ js::GCPtr<T>& getStubField(Stub* stub, uint32_t field) const;
|
|
|
+
|
|
|
+ template <class T>
|
|
|
+ js::GCPtr<T>& getStubField(ICStub* stub, uint32_t field) const {
|
|
|
+ return getStubField<ICStub, T>(stub, field);
|
|
|
+ }
|
|
|
+
|
|
|
++ uintptr_t getStubRawWord(ICStub* stub, uint32_t field) const;
|
|
|
++
|
|
|
+ void copyStubData(ICStub* src, ICStub* dest) const;
|
|
|
+ };
|
|
|
+
|
|
|
+ template <typename T>
|
|
|
+ void TraceCacheIRStub(JSTracer* trc, T* stub, const CacheIRStubInfo* stubInfo);
|
|
|
+
|
|
|
+ } // namespace jit
|
|
|
+ } // namespace js
|
|
|
+diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
|
|
|
+--- a/js/src/jit/CodeGenerator.cpp
|
|
|
++++ b/js/src/jit/CodeGenerator.cpp
|
|
|
+@@ -329,16 +329,17 @@ CodeGenerator::visitOutOfLineICFallback(
|
|
|
+ restoreLiveIgnore(lir, StoreRegisterTo(hasOwnIC->output()).clobbered());
|
|
|
+
|
|
|
+ masm.jump(ool->rejoin());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ case CacheKind::Call:
|
|
|
+ case CacheKind::Compare:
|
|
|
+ case CacheKind::TypeOf:
|
|
|
++ case CacheKind::InstanceOf:
|
|
|
+ MOZ_CRASH("Unsupported IC");
|
|
|
+ }
|
|
|
+ MOZ_CRASH();
|
|
|
+ }
|
|
|
+
|
|
|
+ StringObject*
|
|
|
+ MNewStringObject::templateObj() const
|
|
|
+ {
|
|
|
+diff --git a/js/src/jit/IonCacheIRCompiler.cpp b/js/src/jit/IonCacheIRCompiler.cpp
|
|
|
+--- a/js/src/jit/IonCacheIRCompiler.cpp
|
|
|
++++ b/js/src/jit/IonCacheIRCompiler.cpp
|
|
|
+@@ -529,16 +529,17 @@ IonCacheIRCompiler::init()
|
|
|
+ MOZ_ASSERT(numInputs == 2);
|
|
|
+ allocator.initInputLocation(0, ic->id());
|
|
|
+ allocator.initInputLocation(1, ic->value());
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case CacheKind::Call:
|
|
|
+ case CacheKind::Compare:
|
|
|
+ case CacheKind::TypeOf:
|
|
|
++ case CacheKind::InstanceOf:
|
|
|
+ MOZ_CRASH("Unsupported IC");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (liveRegs_)
|
|
|
+ liveFloatRegs_ = LiveFloatRegisterSet(liveRegs_->fpus());
|
|
|
+
|
|
|
+ allocator.initAvailableRegs(available);
|
|
|
+ allocator.initAvailableRegsAfterSpill();
|
|
|
+@@ -847,16 +848,45 @@ IonCacheIRCompiler::emitGuardXrayExpando
|
|
|
+ masm.branchTestObject(Assembler::Equal, expandoAddress, failure->label());
|
|
|
+ masm.bind(&done);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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()
|
|
|
+ {
|
|
|
+ AutoOutputRegister output(*this);
|
|
|
+ Register obj = allocator.useRegister(masm, reader.objOperandId());
|
|
|
+ int32_t offset = int32StubField(reader.stubOffset());
|
|
|
+ masm.loadTypedOrValue(Address(obj, offset), output);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+diff --git a/js/src/jit/IonIC.cpp b/js/src/jit/IonIC.cpp
|
|
|
+--- a/js/src/jit/IonIC.cpp
|
|
|
++++ b/js/src/jit/IonIC.cpp
|
|
|
+@@ -56,16 +56,17 @@ IonIC::scratchRegisterForEntryJump()
|
|
|
+ return asInIC()->temp();
|
|
|
+ case CacheKind::HasOwn:
|
|
|
+ return asHasOwnIC()->output();
|
|
|
+ case CacheKind::GetIterator:
|
|
|
+ return asGetIteratorIC()->temp1();
|
|
|
+ case CacheKind::Call:
|
|
|
+ case CacheKind::Compare:
|
|
|
+ case CacheKind::TypeOf:
|
|
|
++ case CacheKind::InstanceOf:
|
|
|
+ MOZ_CRASH("Unsupported IC");
|
|
|
+ }
|
|
|
+
|
|
|
+ MOZ_CRASH("Invalid kind");
|
|
|
+ }
|
|
|
+
|
|
|
+ void
|
|
|
+ IonIC::discardStubs(Zone* zone)
|
|
|
+diff --git a/js/src/jit/SharedIC.cpp b/js/src/jit/SharedIC.cpp
|
|
|
+--- a/js/src/jit/SharedIC.cpp
|
|
|
++++ b/js/src/jit/SharedIC.cpp
|
|
|
+@@ -292,22 +292,16 @@ ICStub::trace(JSTracer* trc)
|
|
|
+ TraceEdge(trc, &updateStub->group(), "baseline-update-group");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case ICStub::GetIntrinsic_Constant: {
|
|
|
+ ICGetIntrinsic_Constant* constantStub = toGetIntrinsic_Constant();
|
|
|
+ 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");
|
|
|
+ TraceEdge(trc, &stub->templateGroup(), "baseline-newarray-template-group");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case ICStub::NewObject_Fallback: {
|
|
|
+ ICNewObject_Fallback* stub = toNewObject_Fallback();
|