зеркало из https://github.com/mozilla/gecko-dev.git
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 --HG-- extra : rebase_source : a01db1640d7970dc90530fdf214248b1b076b0ab
This commit is contained in:
Родитель
c8a5a694f9
Коммит
944d7681a3
|
@ -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);
|
|
@ -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);
|
||||
|
|
|
@ -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<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();
|
||||
|
@ -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)
|
||||
|
|
|
@ -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<ICInstanceOf_Function>(space, getStubCode(), shape_, prototypeObj_,
|
||||
slot_);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// TypeOf
|
||||
// JSOP_TYPEOF
|
||||
// JSOP_TYPEOFEXPR
|
||||
|
|
|
@ -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<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;
|
||||
}
|
||||
|
|
|
@ -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<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),
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -863,6 +863,13 @@ 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
|
||||
|
@ -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;
|
||||
}
|
|
@ -47,6 +47,7 @@ namespace jit {
|
|||
_(LoadStringLengthResult) \
|
||||
_(LoadStringCharResult) \
|
||||
_(LoadArgumentsObjectArgResult) \
|
||||
_(LoadInstanceOfObjectResult) \
|
||||
_(LoadDenseElementResult) \
|
||||
_(LoadDenseElementHoleResult) \
|
||||
_(LoadDenseElementExistsResult) \
|
||||
|
@ -712,6 +713,8 @@ class CacheIRStubInfo
|
|||
return getStubField<ICStub, T>(stub, field);
|
||||
}
|
||||
|
||||
uintptr_t getStubRawWord(ICStub* stub, uint32_t field) const;
|
||||
|
||||
void copyStubData(ICStub* src, ICStub* dest) const;
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -61,6 +61,7 @@ IonIC::scratchRegisterForEntryJump()
|
|||
case CacheKind::Call:
|
||||
case CacheKind::Compare:
|
||||
case CacheKind::TypeOf:
|
||||
case CacheKind::InstanceOf:
|
||||
MOZ_CRASH("Unsupported IC");
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
Загрузка…
Ссылка в новой задаче