Bug 1375505 part 9 - Optimize for-in/JSOP_ITER with a Baseline/Ion IC. r=evilpie

This commit is contained in:
Jan de Mooij 2017-07-15 13:22:34 +02:00
Родитель f79c5fa84d
Коммит 2be2d91c31
27 изменённых файлов: 385 добавлений и 322 удалений

Просмотреть файл

@ -1837,6 +1837,54 @@ BaselineCacheIRCompiler::emitLoadStackValue()
return true;
}
bool
BaselineCacheIRCompiler::emitGuardAndGetIterator()
{
Register obj = allocator.useRegister(masm, reader.objOperandId());
AutoScratchRegister scratch1(allocator, masm);
AutoScratchRegister scratch2(allocator, masm);
AutoScratchRegister niScratch(allocator, masm);
Address iterAddr(stubAddress(reader.stubOffset()));
Address enumeratorsAddr(stubAddress(reader.stubOffset()));
Register output = allocator.defineRegister(masm, reader.objOperandId());
FailurePath* failure;
if (!addFailurePath(&failure))
return false;
// Load our PropertyIteratorObject* and its NativeIterator.
masm.loadPtr(iterAddr, output);
masm.loadObjPrivate(output, JSObject::ITER_CLASS_NFIXED_SLOTS, niScratch);
// Ensure the |active| and |unreusable| bits are not set.
masm.branchTest32(Assembler::NonZero, Address(niScratch, offsetof(NativeIterator, flags)),
Imm32(JSITER_ACTIVE|JSITER_UNREUSABLE), failure->label());
// Pre-write barrier for store to 'obj'.
Address iterObjAddr(niScratch, offsetof(NativeIterator, obj));
EmitPreBarrier(masm, iterObjAddr, MIRType::Object);
// Mark iterator as active.
Address iterFlagsAddr(niScratch, offsetof(NativeIterator, flags));
masm.storePtr(obj, iterObjAddr);
masm.or32(Imm32(JSITER_ACTIVE), iterFlagsAddr);
// Post-write barrier for stores to 'obj'.
emitPostBarrierSlot(output, TypedOrValueRegister(MIRType::Object, AnyRegister(obj)), scratch1);
// Chain onto the active iterator stack. Note that Baseline CacheIR stub
// code is shared across compartments within a Zone, so we can't bake in
// compartment->enumerators here.
masm.loadPtr(enumeratorsAddr, scratch1);
masm.loadPtr(Address(scratch1, 0), scratch1);
emitRegisterEnumerator(scratch1, niScratch, scratch2);
return true;
}
bool
BaselineCacheIRCompiler::emitGuardDOMExpandoMissingOrGuardShape()
{
@ -1913,6 +1961,7 @@ BaselineCacheIRCompiler::init(CacheKind kind)
switch (kind) {
case CacheKind::GetProp:
case CacheKind::TypeOf:
case CacheKind::GetIterator:
MOZ_ASSERT(numInputs == 1);
allocator.initInputLocation(0, R0);
break;
@ -1992,6 +2041,7 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
case CacheKind::HasOwn:
case CacheKind::BindName:
case CacheKind::TypeOf:
case CacheKind::GetIterator:
stubDataOffset = sizeof(ICCacheIR_Regular);
stubKind = CacheIRStubKind::Regular;
break;

Просмотреть файл

@ -4201,7 +4201,7 @@ BaselineCompiler::emit_JSOP_ITER()
{
frame.popRegsAndSync(1);
ICIteratorNew_Fallback::Compiler compiler(cx);
ICGetIterator_Fallback::Compiler compiler(cx);
if (!emitOpIC(compiler.getStub(&stubSpace_)))
return false;

Просмотреть файл

@ -4148,33 +4148,53 @@ ICTableSwitch::fixupJumpTable(JSScript* script, BaselineScript* baseline)
}
//
// IteratorNew_Fallback
// GetIterator_Fallback
//
static bool
DoIteratorNewFallback(JSContext* cx, BaselineFrame* frame, ICIteratorNew_Fallback* stub,
DoGetIteratorFallback(JSContext* cx, BaselineFrame* frame, ICGetIterator_Fallback* stub,
HandleValue value, MutableHandleValue res)
{
jsbytecode* pc = stub->icEntry()->pc(frame->script());
FallbackICSpew(cx, stub, "IteratorNew");
FallbackICSpew(cx, stub, "GetIterator");
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;
GetIteratorIRGenerator gen(cx, script, pc, stub->state().mode(), value);
bool attached = false;
if (gen.tryAttachStub()) {
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
engine, script, stub, &attached);
if (newStub)
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
}
if (!attached)
stub->state().trackNotAttached();
}
uint8_t flags = GET_UINT8(pc);
res.set(value);
RootedObject iterobj(cx, ValueToIterator(cx, flags, res));
JSObject* iterobj = ValueToIterator(cx, flags, value);
if (!iterobj)
return false;
res.setObject(*iterobj);
return true;
}
typedef bool (*DoIteratorNewFallbackFn)(JSContext*, BaselineFrame*, ICIteratorNew_Fallback*,
typedef bool (*DoGetIteratorFallbackFn)(JSContext*, BaselineFrame*, ICGetIterator_Fallback*,
HandleValue, MutableHandleValue);
static const VMFunction DoIteratorNewFallbackInfo =
FunctionInfo<DoIteratorNewFallbackFn>(DoIteratorNewFallback, "DoIteratorNewFallback",
static const VMFunction DoGetIteratorFallbackInfo =
FunctionInfo<DoGetIteratorFallbackFn>(DoGetIteratorFallback, "DoGetIteratorFallback",
TailCall, PopValues(1));
bool
ICIteratorNew_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
ICGetIterator_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
{
MOZ_ASSERT(engine_ == Engine::Baseline);
@ -4187,7 +4207,7 @@ ICIteratorNew_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
masm.push(ICStubReg);
pushStubPayload(masm, R0.scratchReg());
return tailCallVM(DoIteratorNewFallbackInfo, masm);
return tailCallVM(DoGetIteratorFallbackInfo, masm);
}
//

Просмотреть файл

@ -1359,12 +1359,12 @@ class ICTableSwitch : public ICStub
};
// IC for constructing an iterator from an input value.
class ICIteratorNew_Fallback : public ICFallbackStub
class ICGetIterator_Fallback : public ICFallbackStub
{
friend class ICStubSpace;
explicit ICIteratorNew_Fallback(JitCode* stubCode)
: ICFallbackStub(ICStub::IteratorNew_Fallback, stubCode)
explicit ICGetIterator_Fallback(JitCode* stubCode)
: ICFallbackStub(ICStub::GetIterator_Fallback, stubCode)
{ }
public:
@ -1374,11 +1374,11 @@ class ICIteratorNew_Fallback : public ICFallbackStub
public:
explicit Compiler(JSContext* cx)
: ICStubCompiler(cx, ICStub::IteratorNew_Fallback, Engine::Baseline)
: ICStubCompiler(cx, ICStub::GetIterator_Fallback, Engine::Baseline)
{ }
ICStub* getStub(ICStubSpace* space) {
return newStub<ICIteratorNew_Fallback>(space, getStubCode());
return newStub<ICGetIterator_Fallback>(space, getStubCode());
}
};
};

Просмотреть файл

@ -67,7 +67,7 @@ namespace jit {
\
_(TableSwitch) \
\
_(IteratorNew_Fallback) \
_(GetIterator_Fallback) \
_(IteratorMore_Fallback) \
_(IteratorMore_Native) \
_(IteratorClose_Fallback) \

Просмотреть файл

@ -3571,6 +3571,69 @@ TypeOfIRGenerator::tryAttachObject(ValOperandId valId)
return true;
}
GetIteratorIRGenerator::GetIteratorIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
ICState::Mode mode, HandleValue value)
: IRGenerator(cx, script, pc, CacheKind::GetIterator, mode),
val_(value)
{ }
bool
GetIteratorIRGenerator::tryAttachStub()
{
MOZ_ASSERT(cacheKind_ == CacheKind::GetIterator);
AutoAssertNoPendingException aanpe(cx_);
if (mode_ == ICState::Mode::Megamorphic)
return false;
ValOperandId valId(writer.setInputOperandId(0));
if (!val_.isObject())
return false;
RootedObject obj(cx_, &val_.toObject());
ObjOperandId objId = writer.guardIsObject(valId);
if (tryAttachNativeIterator(objId, obj))
return true;
return false;
}
bool
GetIteratorIRGenerator::tryAttachNativeIterator(ObjOperandId objId, HandleObject obj)
{
MOZ_ASSERT(JSOp(*pc_) == JSOP_ITER);
if (GET_UINT8(pc_) != JSITER_ENUMERATE)
return false;
PropertyIteratorObject* iterobj = LookupInIteratorCache(cx_, obj);
if (!iterobj)
return false;
MOZ_ASSERT(obj->isNative() || obj->is<UnboxedPlainObject>());
// Guard on the receiver's shape/group.
Maybe<ObjOperandId> expandoId;
TestMatchingReceiver(writer, obj, objId, &expandoId);
// Ensure the receiver or its expando object has no dense elements.
if (obj->isNative())
writer.guardNoDenseElements(objId);
else if (expandoId)
writer.guardNoDenseElements(*expandoId);
// Do the same for the objects on the proto chain.
GeneratePrototypeHoleGuards(writer, obj, objId);
ObjOperandId iterId =
writer.guardAndGetIterator(objId, iterobj, &cx_->compartment()->enumerators);
writer.loadObjectResult(iterId);
writer.returnFromIC();
return true;
}
CallIRGenerator::CallIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
ICState::Mode mode, uint32_t argc,

Просмотреть файл

@ -145,6 +145,7 @@ class TypedOperandId : public OperandId
_(In) \
_(HasOwn) \
_(TypeOf) \
_(GetIterator) \
_(Compare) \
_(Call)
@ -184,6 +185,7 @@ extern const char* CacheKindNames[];
_(GuardNoUnboxedExpando) \
_(GuardAndLoadUnboxedExpando) \
_(GuardAndGetIndexFromString) \
_(GuardAndGetIterator) \
_(GuardHasGetterSetter) \
_(GuardGroupHasUnanalyzedNewScript) \
_(LoadStackValue) \
@ -583,6 +585,16 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
writeOperandId(res);
return res;
}
ObjOperandId guardAndGetIterator(ObjOperandId obj, PropertyIteratorObject* iter,
NativeIterator** enumeratorsAddr)
{
ObjOperandId res(nextOperandId_++);
writeOpWithOperandId(CacheOp::GuardAndGetIterator, obj);
addStubField(uintptr_t(iter), StubField::Type::JSObject);
addStubField(uintptr_t(enumeratorsAddr), StubField::Type::RawWord);
writeOperandId(res);
return res;
}
void guardHasGetterSetter(ObjOperandId obj, Shape* shape) {
writeOpWithOperandId(CacheOp::GuardHasGetterSetter, obj);
@ -1408,6 +1420,19 @@ class MOZ_RAII TypeOfIRGenerator : public IRGenerator
bool tryAttachStub();
};
class MOZ_RAII GetIteratorIRGenerator : public IRGenerator
{
HandleValue val_;
bool tryAttachNativeIterator(ObjOperandId objId, HandleObject obj);
public:
GetIteratorIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode mode,
HandleValue value);
bool tryAttachStub();
};
class MOZ_RAII CallIRGenerator : public IRGenerator
{
public:

Просмотреть файл

@ -2330,6 +2330,23 @@ CacheIRCompiler::emitStoreTypedObjectReferenceProp(ValueOperand val, ReferenceTy
}
}
void
CacheIRCompiler::emitRegisterEnumerator(Register enumeratorsList, Register iter, Register scratch)
{
// iter->next = list
masm.storePtr(enumeratorsList, Address(iter, NativeIterator::offsetOfNext()));
// iter->prev = list->prev
masm.loadPtr(Address(enumeratorsList, NativeIterator::offsetOfPrev()), scratch);
masm.storePtr(scratch, Address(iter, NativeIterator::offsetOfPrev()));
// list->prev->next = iter
masm.storePtr(iter, Address(scratch, NativeIterator::offsetOfNext()));
// list->prev = ni
masm.storePtr(iter, Address(enumeratorsList, NativeIterator::offsetOfPrev()));
}
void
CacheIRCompiler::emitPostBarrierShared(Register obj, const ConstantOrRegister& val,
Register scratch, Register maybeIndex)

Просмотреть файл

@ -597,6 +597,8 @@ class MOZ_RAII CacheIRCompiler
void emitStoreTypedObjectReferenceProp(ValueOperand val, ReferenceTypeDescr::Type type,
const Address& dest, Register scratch);
void emitRegisterEnumerator(Register enumeratorsList, Register iter, Register scratch);
private:
void emitPostBarrierShared(Register obj, const ConstantOrRegister& val, Register scratch,
Register maybeIndex);

Просмотреть файл

@ -160,6 +160,10 @@ typedef JSObject* (*IonBindNameICFn)(JSContext*, HandleScript, IonBindNameIC*, H
static const VMFunction IonBindNameICInfo =
FunctionInfo<IonBindNameICFn>(IonBindNameIC::update, "IonBindNameIC::update");
typedef JSObject* (*IonGetIteratorICFn)(JSContext*, HandleScript, IonGetIteratorIC*, HandleValue);
static const VMFunction IonGetIteratorICInfo =
FunctionInfo<IonGetIteratorICFn>(IonGetIteratorIC::update, "IonGetIteratorIC::update");
typedef bool (*IonInICFn)(JSContext*, HandleScript, IonInIC*, HandleValue, HandleObject, bool*);
static const VMFunction IonInICInfo =
FunctionInfo<IonInICFn>(IonInIC::update, "IonInIC::update");
@ -249,6 +253,23 @@ CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool)
masm.jump(ool->rejoin());
return;
}
case CacheKind::GetIterator: {
IonGetIteratorIC* getIteratorIC = ic->asGetIteratorIC();
saveLive(lir);
pushArg(getIteratorIC->value());
icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
pushArg(ImmGCPtr(gen->info().script()));
callVM(IonGetIteratorICInfo, lir);
StoreRegisterTo(getIteratorIC->output()).generate(this);
restoreLiveIgnore(lir, StoreRegisterTo(getIteratorIC->output()).clobbered());
masm.jump(ool->rejoin());
return;
}
case CacheKind::In: {
IonInIC* inIC = ic->asInIC();
@ -9244,163 +9265,18 @@ CodeGenerator::visitArrayJoin(LArrayJoin* lir)
callVM(ArrayJoinInfo, lir);
}
typedef JSObject* (*ValueToIteratorFn)(JSContext*, uint32_t, HandleValue);
static const VMFunction ValueToIteratorInfo =
FunctionInfo<ValueToIteratorFn>(ValueToIterator, "ValueToIterator");
void
CodeGenerator::visitCallIteratorStartV(LCallIteratorStartV* lir)
CodeGenerator::visitGetIteratorCache(LGetIteratorCache* lir)
{
pushArg(ToValue(lir, LCallIteratorStartV::Value));
pushArg(Imm32(lir->mir()->flags()));
callVM(ValueToIteratorInfo, lir);
}
LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
TypedOrValueRegister val =
toConstantOrRegister(lir, LGetIteratorCache::Value, lir->mir()->value()->type()).reg();
Register output = ToRegister(lir->output());
Register temp1 = ToRegister(lir->temp1());
Register temp2 = ToRegister(lir->temp2());
typedef JSObject* (*GetIteratorFn)(JSContext*, HandleObject, uint32_t);
static const VMFunction GetIteratorInfo =
FunctionInfo<GetIteratorFn>(GetIterator, "GetIterator");
void
CodeGenerator::visitCallIteratorStartO(LCallIteratorStartO* lir)
{
pushArg(Imm32(lir->mir()->flags()));
pushArg(ToRegister(lir->object()));
callVM(GetIteratorInfo, lir);
}
void
CodeGenerator::branchIfNotEmptyObjectElements(Register obj, Label* target)
{
Label emptyObj;
masm.branchPtr(Assembler::Equal,
Address(obj, NativeObject::offsetOfElements()),
ImmPtr(js::emptyObjectElements),
&emptyObj);
masm.branchPtr(Assembler::NotEqual,
Address(obj, NativeObject::offsetOfElements()),
ImmPtr(js::emptyObjectElementsShared),
target);
masm.bind(&emptyObj);
}
void
CodeGenerator::visitIteratorStartO(LIteratorStartO* lir)
{
const Register obj = ToRegister(lir->object());
const Register output = ToRegister(lir->output());
uint32_t flags = lir->mir()->flags();
OutOfLineCode* ool = oolCallVM(GetIteratorInfo, lir,
ArgList(obj, Imm32(flags)), StoreRegisterTo(output));
const Register temp1 = ToRegister(lir->temp1());
const Register temp2 = ToRegister(lir->temp2());
const Register niTemp = ToRegister(lir->temp3()); // Holds the NativeIterator object.
// Iterators other than for-in should use LCallIteratorStart.
MOZ_ASSERT(flags == JSITER_ENUMERATE);
// Fetch the most recent iterator and ensure it's not nullptr.
masm.loadPtr(AbsoluteAddress(gen->compartment->addressOfLastCachedNativeIterator()), output);
masm.branchTestPtr(Assembler::Zero, output, output, ool->entry());
// Load NativeIterator.
masm.loadObjPrivate(output, JSObject::ITER_CLASS_NFIXED_SLOTS, niTemp);
// Ensure the |active| and |unreusable| bits are not set.
masm.branchTest32(Assembler::NonZero, Address(niTemp, offsetof(NativeIterator, flags)),
Imm32(JSITER_ACTIVE|JSITER_UNREUSABLE), ool->entry());
// Load the iterator's receiver guard array.
masm.loadPtr(Address(niTemp, offsetof(NativeIterator, guard_array)), temp2);
// Compare object with the first receiver guard. The last iterator can only
// match for native objects and unboxed objects.
{
Address groupAddr(temp2, offsetof(ReceiverGuard, group));
Address shapeAddr(temp2, offsetof(ReceiverGuard, shape));
Label guardDone, unboxedObject, noExpando;
// This is a guard for an unboxed object.
masm.branchPtr(Assembler::NotEqual, groupAddr, ImmWord(0), &unboxedObject);
// Guard for a normal object, make sure the shape matches.
masm.loadObjShape(obj, temp1);
masm.branchPtr(Assembler::NotEqual, shapeAddr, temp1, ool->entry());
// Ensure the object does not have any elements. The presence of dense
// elements is not captured by the shape tests above.
branchIfNotEmptyObjectElements(obj, ool->entry());
masm.jump(&guardDone);
masm.bind(&unboxedObject);
masm.loadObjGroup(obj, temp1);
masm.branchPtr(Assembler::NotEqual, groupAddr, temp1, ool->entry());
masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), temp1);
masm.branchTestPtr(Assembler::Zero, temp1, temp1, &noExpando);
branchIfNotEmptyObjectElements(temp1, ool->entry());
masm.loadObjShape(temp1, temp1);
masm.bind(&noExpando);
masm.branchPtr(Assembler::NotEqual, shapeAddr, temp1, ool->entry());
masm.bind(&guardDone);
}
// Compare shape of object's prototype with the second shape. The prototype
// must be native, as unboxed objects cannot be prototypes (they cannot
// have the delegate flag set). Also check for the absence of dense elements.
Address prototypeShapeAddr(temp2, sizeof(ReceiverGuard) + offsetof(ReceiverGuard, shape));
masm.loadObjProto(obj, temp1);
branchIfNotEmptyObjectElements(temp1, ool->entry());
masm.loadObjShape(temp1, temp1);
masm.branchPtr(Assembler::NotEqual, prototypeShapeAddr, temp1, ool->entry());
// Ensure the object's prototype's prototype is nullptr. The last native
// iterator will always have a prototype chain length of one (i.e. it must
// be a plain object), so we do not need to generate a loop here.
masm.loadObjProto(obj, temp1);
masm.loadObjProto(temp1, temp1);
masm.branchTestPtr(Assembler::NonZero, temp1, temp1, ool->entry());
// Pre-write barrier for store to 'obj'.
masm.guardedCallPreBarrier(Address(niTemp, offsetof(NativeIterator, obj)), MIRType::Object);
// Mark iterator as active.
masm.storePtr(obj, Address(niTemp, offsetof(NativeIterator, obj)));
masm.or32(Imm32(JSITER_ACTIVE), Address(niTemp, offsetof(NativeIterator, flags)));
// Post-write barrier for stores to 'obj'. The iterator JSObject is never
// nursery allocated. Put this in the whole cell buffer is we wrote a
// nursery pointer into it.
{
Label skipBarrier;
masm.branchPtrInNurseryChunk(Assembler::NotEqual, obj, temp1, &skipBarrier);
LiveRegisterSet temps;
temps.add(temp1);
temps.add(temp2);
saveVolatile(temps);
emitPostWriteBarrier(output);
restoreVolatile(temps);
masm.bind(&skipBarrier);
}
// Chain onto the active iterator stack.
masm.loadPtr(AbsoluteAddress(gen->compartment->addressOfEnumerators()), temp1);
// ni->next = list
masm.storePtr(temp1, Address(niTemp, NativeIterator::offsetOfNext()));
// ni->prev = list->prev
masm.loadPtr(Address(temp1, NativeIterator::offsetOfPrev()), temp2);
masm.storePtr(temp2, Address(niTemp, NativeIterator::offsetOfPrev()));
// list->prev->next = ni
masm.storePtr(niTemp, Address(temp2, NativeIterator::offsetOfNext()));
// list->prev = ni
masm.storePtr(niTemp, Address(temp1, NativeIterator::offsetOfPrev()));
masm.bind(ool->rejoin());
IonGetIteratorIC ic(liveRegs, val, output, temp1, temp2);
addIC(lir, allocateIC(ic));
}
static void

Просмотреть файл

@ -336,9 +336,7 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitClampIToUint8(LClampIToUint8* lir);
void visitClampDToUint8(LClampDToUint8* lir);
void visitClampVToUint8(LClampVToUint8* lir);
void visitCallIteratorStartV(LCallIteratorStartV* lir);
void visitCallIteratorStartO(LCallIteratorStartO* lir);
void visitIteratorStartO(LIteratorStartO* lir);
void visitGetIteratorCache(LGetIteratorCache* lir);
void visitIteratorMore(LIteratorMore* lir);
void visitIsNoIterAndBranch(LIsNoIterAndBranch* lir);
void visitIteratorEnd(LIteratorEnd* lir);
@ -537,10 +535,6 @@ class CodeGenerator final : public CodeGeneratorSpecific
Label* ifDoesntEmulateUndefined,
Register scratch, OutOfLineTestObject* ool);
// Branch to target unless obj has an emptyObjectElements or emptyObjectElementsShared
// elements pointer.
void branchIfNotEmptyObjectElements(Register obj, Label* target);
void emitStoreElementTyped(const LAllocation* value, MIRType valueType, MIRType elementType,
Register elements, const LAllocation* index,
int32_t offsetAdjustment);

Просмотреть файл

@ -223,18 +223,6 @@ CompileCompartment::runtime()
return CompileRuntime::get(compartment()->runtimeFromAnyThread());
}
const void*
CompileCompartment::addressOfEnumerators()
{
return &compartment()->enumerators;
}
const void*
CompileCompartment::addressOfLastCachedNativeIterator()
{
return &compartment()->lastCachedNativeIterator;
}
const void*
CompileCompartment::addressOfRandomNumberGenerator()
{

Просмотреть файл

@ -94,9 +94,7 @@ class CompileCompartment
CompileZone* zone();
CompileRuntime* runtime();
const void* addressOfEnumerators();
const void* addressOfRandomNumberGenerator();
const void* addressOfLastCachedNativeIterator();
const JitCompartment* jitCompartment();

Просмотреть файл

@ -12497,7 +12497,7 @@ IonBuilder::jsop_iter(uint8_t flags)
nonStringIteration_ = true;
MDefinition* obj = current->pop();
MInstruction* ins = MIteratorStart::New(alloc(), obj, flags);
MInstruction* ins = MGetIteratorCache::New(alloc(), obj);
if (!outermostBuilder()->iterators_.append(ins))
return abort(AbortReason::Alloc);

Просмотреть файл

@ -440,6 +440,21 @@ IonCacheIRCompiler::init()
allocator.initInputLocation(0, ic->environment(), JSVAL_TYPE_OBJECT);
break;
}
case CacheKind::GetIterator: {
IonGetIteratorIC* ic = ic_->asGetIteratorIC();
Register output = ic->output();
available.add(output);
available.add(ic->temp1());
available.add(ic->temp2());
liveRegs_.emplace(ic->liveRegs());
outputUnchecked_.emplace(TypedOrValueRegister(MIRType::Object, AnyRegister(output)));
MOZ_ASSERT(numInputs == 1);
allocator.initInputLocation(0, ic->value());
break;
}
case CacheKind::In: {
IonInIC* ic = ic_->asInIC();
Register output = ic->output();
@ -2006,6 +2021,52 @@ IonCacheIRCompiler::emitLoadStackValue()
return false;
}
bool
IonCacheIRCompiler::emitGuardAndGetIterator()
{
Register obj = allocator.useRegister(masm, reader.objOperandId());
AutoScratchRegister scratch1(allocator, masm);
AutoScratchRegister scratch2(allocator, masm);
AutoScratchRegister niScratch(allocator, masm);
PropertyIteratorObject* iterobj =
&objectStubField(reader.stubOffset())->as<PropertyIteratorObject>();
NativeIterator** enumerators = rawWordStubField<NativeIterator**>(reader.stubOffset());
Register output = allocator.defineRegister(masm, reader.objOperandId());
FailurePath* failure;
if (!addFailurePath(&failure))
return false;
// Load our PropertyIteratorObject* and its NativeIterator.
masm.movePtr(ImmGCPtr(iterobj), output);
masm.loadObjPrivate(output, JSObject::ITER_CLASS_NFIXED_SLOTS, niScratch);
// Ensure the |active| and |unreusable| bits are not set.
masm.branchTest32(Assembler::NonZero, Address(niScratch, offsetof(NativeIterator, flags)),
Imm32(JSITER_ACTIVE|JSITER_UNREUSABLE), failure->label());
// Pre-write barrier for store to 'obj'.
Address iterObjAddr(niScratch, offsetof(NativeIterator, obj));
EmitPreBarrier(masm, iterObjAddr, MIRType::Object);
// Mark iterator as active.
Address iterFlagsAddr(niScratch, offsetof(NativeIterator, flags));
masm.storePtr(obj, iterObjAddr);
masm.or32(Imm32(JSITER_ACTIVE), iterFlagsAddr);
// Post-write barrier for stores to 'obj'.
emitPostBarrierSlot(output, TypedOrValueRegister(MIRType::Object, AnyRegister(obj)), scratch1);
// Chain onto the active iterator stack.
masm.loadPtr(AbsoluteAddress(enumerators), scratch1);
emitRegisterEnumerator(scratch1, niScratch, scratch2);
return true;
}
bool
IonCacheIRCompiler::emitGuardDOMExpandoMissingOrGuardShape()
{

Просмотреть файл

@ -52,6 +52,8 @@ 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:
@ -346,6 +348,31 @@ IonBindNameIC::update(JSContext* cx, HandleScript outerScript, IonBindNameIC* ic
return holder;
}
/* static */ JSObject*
IonGetIteratorIC::update(JSContext* cx, HandleScript outerScript, IonGetIteratorIC* ic,
HandleValue value)
{
IonScript* ionScript = outerScript->ionScript();
jsbytecode* pc = ic->pc();
if (ic->state().maybeTransition())
ic->discardStubs(cx->zone());
if (ic->state().canAttachStub()) {
bool attached = false;
RootedScript script(cx, ic->script());
GetIteratorIRGenerator gen(cx, script, pc, ic->state().mode(), value);
if (gen.tryAttachStub())
ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
if (!attached)
ic->state().trackNotAttached();
}
uint8_t flags = GET_UINT8(pc);
return ValueToIterator(cx, flags, value);
}
/* static */ bool
IonHasOwnIC::update(JSContext* cx, HandleScript outerScript, IonHasOwnIC* ic,
HandleValue val, HandleValue idVal, int32_t* res)

Просмотреть файл

@ -60,6 +60,7 @@ class IonGetPropertyIC;
class IonSetPropertyIC;
class IonGetNameIC;
class IonBindNameIC;
class IonGetIteratorIC;
class IonHasOwnIC;
class IonInIC;
@ -149,6 +150,10 @@ class IonIC
MOZ_ASSERT(kind_ == CacheKind::BindName);
return (IonBindNameIC*)this;
}
IonGetIteratorIC* asGetIteratorIC() {
MOZ_ASSERT(kind_ == CacheKind::GetIterator);
return (IonGetIteratorIC*)this;
}
IonHasOwnIC* asHasOwnIC() {
MOZ_ASSERT(kind_ == CacheKind::HasOwn);
return (IonHasOwnIC*)this;
@ -314,6 +319,35 @@ class IonBindNameIC : public IonIC
HandleObject envChain);
};
class IonGetIteratorIC : public IonIC
{
LiveRegisterSet liveRegs_;
TypedOrValueRegister value_;
Register output_;
Register temp1_;
Register temp2_;
public:
IonGetIteratorIC(LiveRegisterSet liveRegs, TypedOrValueRegister value, Register output,
Register temp1, Register temp2)
: IonIC(CacheKind::GetIterator),
liveRegs_(liveRegs),
value_(value),
output_(output),
temp1_(temp1),
temp2_(temp2)
{ }
TypedOrValueRegister value() const { return value_; }
Register output() const { return output_; }
Register temp1() const { return temp1_; }
Register temp2() const { return temp2_; }
LiveRegisterSet liveRegs() const { return liveRegs_; }
static JSObject* update(JSContext* cx, HandleScript outerScript, IonGetIteratorIC* ic,
HandleValue value);
};
class IonHasOwnIC : public IonIC
{
LiveRegisterSet liveRegs_;

Просмотреть файл

@ -4057,27 +4057,14 @@ LIRGenerator::visitCallInitElementArray(MCallInitElementArray* ins)
}
void
LIRGenerator::visitIteratorStart(MIteratorStart* ins)
LIRGenerator::visitGetIteratorCache(MGetIteratorCache* ins)
{
if (ins->object()->type() == MIRType::Value) {
LCallIteratorStartV* lir = new(alloc()) LCallIteratorStartV(useBoxAtStart(ins->object()));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
return;
}
MDefinition* value = ins->value();
MOZ_ASSERT(value->type() == MIRType::Object || value->type() == MIRType::Value);
MOZ_ASSERT(ins->object()->type() == MIRType::Object);
// Call a stub if this is not a simple for-in loop.
if (ins->flags() != JSITER_ENUMERATE) {
LCallIteratorStartO* lir = new(alloc()) LCallIteratorStartO(useRegisterAtStart(ins->object()));
defineReturn(lir, ins);
assignSafepoint(lir, ins);
} else {
LIteratorStartO* lir = new(alloc()) LIteratorStartO(useRegister(ins->object()), temp(), temp(), temp());
define(lir, ins);
assignSafepoint(lir, ins);
}
LGetIteratorCache* lir = new(alloc()) LGetIteratorCache(useBoxOrTyped(value), temp(), temp());
define(lir, ins);
assignSafepoint(lir, ins);
}
void

Просмотреть файл

@ -275,7 +275,7 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitCallInitElementArray(MCallInitElementArray* ins);
void visitSetPropertyCache(MSetPropertyCache* ins);
void visitCallSetProperty(MCallSetProperty* ins);
void visitIteratorStart(MIteratorStart* ins);
void visitGetIteratorCache(MGetIteratorCache* ins);
void visitIteratorMore(MIteratorMore* ins);
void visitIsNoIter(MIsNoIter* ins);
void visitIteratorEnd(MIteratorEnd* ins);

Просмотреть файл

@ -12472,26 +12472,20 @@ class MNearbyInt
ALLOW_CLONE(MNearbyInt)
};
class MIteratorStart
class MGetIteratorCache
: public MUnaryInstruction,
public BoxExceptPolicy<0, MIRType::Object>::Data
{
uint8_t flags_;
MIteratorStart(MDefinition* obj, uint8_t flags)
: MUnaryInstruction(obj), flags_(flags)
explicit MGetIteratorCache(MDefinition* val)
: MUnaryInstruction(val)
{
setResultType(MIRType::Object);
}
public:
INSTRUCTION_HEADER(IteratorStart)
INSTRUCTION_HEADER(GetIteratorCache)
TRIVIAL_NEW_WRAPPERS
NAMED_OPERANDS((0, object))
uint8_t flags() const {
return flags_;
}
NAMED_OPERANDS((0, value))
};
class MIteratorMore

Просмотреть файл

@ -252,7 +252,7 @@ namespace jit {
_(DeleteProperty) \
_(DeleteElement) \
_(SetPropertyCache) \
_(IteratorStart) \
_(GetIteratorCache) \
_(IteratorMore) \
_(IsNoIter) \
_(IteratorEnd) \

Просмотреть файл

@ -7340,51 +7340,22 @@ class LSetPropertyCache : public LInstructionHelper<0, 1 + 2 * BOX_PIECES, 3>
}
};
class LCallIteratorStartV : public LCallInstructionHelper<1, BOX_PIECES, 0>
class LGetIteratorCache : public LInstructionHelper<1, BOX_PIECES, 2>
{
public:
LIR_HEADER(CallIteratorStartV)
LIR_HEADER(GetIteratorCache)
static const size_t Value = 0;
explicit LCallIteratorStartV(const LBoxAllocation& value) {
LGetIteratorCache(const LBoxAllocation& value, const LDefinition& temp1,
const LDefinition& temp2)
{
setBoxOperand(Value, value);
}
MIteratorStart* mir() const {
return mir_->toIteratorStart();
}
};
class LCallIteratorStartO : public LCallInstructionHelper<1, 1, 0>
{
public:
LIR_HEADER(CallIteratorStartO)
explicit LCallIteratorStartO(const LAllocation& object) {
setOperand(0, object);
}
const LAllocation* object() {
return getOperand(0);
}
MIteratorStart* mir() const {
return mir_->toIteratorStart();
}
};
class LIteratorStartO : public LInstructionHelper<1, 1, 3>
{
public:
LIR_HEADER(IteratorStartO)
LIteratorStartO(const LAllocation& object, const LDefinition& temp1,
const LDefinition& temp2, const LDefinition& temp3) {
setOperand(0, object);
setTemp(0, temp1);
setTemp(1, temp2);
setTemp(2, temp3);
}
const LAllocation* object() {
return getOperand(0);
const MGetIteratorCache* mir() const {
return mir_->toGetIteratorCache();
}
const LDefinition* temp1() {
return getTemp(0);
@ -7392,12 +7363,6 @@ class LIteratorStartO : public LInstructionHelper<1, 1, 3>
const LDefinition* temp2() {
return getTemp(1);
}
const LDefinition* temp3() {
return getTemp(2);
}
MIteratorStart* mir() const {
return mir_->toIteratorStart();
}
};
class LIteratorMore : public LInstructionHelper<BOX_PIECES, 1, 1>

Просмотреть файл

@ -339,9 +339,7 @@
_(SetPropertyCache) \
_(SetPropertyPolymorphicV) \
_(SetPropertyPolymorphicT) \
_(CallIteratorStartV) \
_(CallIteratorStartO) \
_(IteratorStartO) \
_(GetIteratorCache) \
_(IteratorMore) \
_(IsNoIterAndBranch) \
_(IteratorEnd) \

Просмотреть файл

@ -89,7 +89,6 @@ JSCompartment::JSCompartment(Zone* zone, const JS::CompartmentOptions& options =
debugScriptMap(nullptr),
debugEnvs(nullptr),
enumerators(nullptr),
lastCachedNativeIterator(nullptr),
compartmentStats_(nullptr),
scheduledForDestruction(false),
maybeAlive(true),
@ -1069,7 +1068,6 @@ JSCompartment::purge()
{
dtoaCache.purge();
newProxyCache.purge();
lastCachedNativeIterator = nullptr;
objectGroups.purge();
}

Просмотреть файл

@ -1129,9 +1129,6 @@ struct JSCompartment
*/
js::NativeIterator* enumerators;
/* Native iterator most recently started. */
js::PropertyIteratorObject* lastCachedNativeIterator;
private:
/* Used by memory reporters and invalid otherwise. */
JS::CompartmentStats* compartmentStats_;

Просмотреть файл

@ -865,24 +865,6 @@ CanCompareIterableObjectToCache(JSObject* obj)
return false;
}
static MOZ_ALWAYS_INLINE void
UpdateLastCachedNativeIterator(JSContext* cx, JSObject* obj, PropertyIteratorObject* iterobj)
{
// lastCachedNativeIterator is only used when there's a single object on
// the prototype chain, to simplify JIT code.
if (iterobj->getNativeIterator()->guard_length != 2)
return;
// Both GetIterator and JIT code assume the receiver has a non-null proto,
// so we have to make sure a Shape change is triggered when the proto
// changes. Note that this does not apply to the object on the proto chain
// because we always check it has a null proto.
if (obj->hasUncacheableProto())
return;
cx->compartment()->lastCachedNativeIterator = iterobj;
}
using ReceiverGuardVector = Vector<ReceiverGuard, 8>;
static MOZ_ALWAYS_INLINE PropertyIteratorObject*
@ -932,11 +914,6 @@ LookupInIteratorCache(JSContext* cx, JSObject* obj, ReceiverGuardVector& guards,
return nullptr;
}
UpdateNativeIterator(ni, obj);
RegisterEnumerator(cx, iterobj, ni);
UpdateLastCachedNativeIterator(cx, obj, iterobj);
return iterobj;
}
@ -974,8 +951,6 @@ StoreInIteratorCache(JSContext* cx, JSObject* obj, uint32_t key, PropertyIterato
MOZ_ASSERT(iterobj->getNativeIterator()->guard_length > 0);
cx->caches().nativeIterCache.set(key, iterobj);
UpdateLastCachedNativeIterator(cx, obj, iterobj);
}
JSObject*
@ -984,30 +959,13 @@ js::GetIterator(JSContext* cx, HandleObject obj, unsigned flags)
ReceiverGuardVector guards(cx);
uint32_t key = 0;
if (flags == JSITER_ENUMERATE) {
// Check to see if this is the same as the most recent object which was
// iterated over.
if (PropertyIteratorObject* last = cx->compartment()->lastCachedNativeIterator) {
NativeIterator* lastni = last->getNativeIterator();
if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
CanCompareIterableObjectToCache(obj) &&
ReceiverGuard(obj) == lastni->guard_array[0])
{
JSObject* proto = obj->staticPrototype();
if (CanCompareIterableObjectToCache(proto) &&
ReceiverGuard(proto) == lastni->guard_array[1] &&
!proto->staticPrototype())
{
assertSameCompartment(cx, last);
UpdateNativeIterator(lastni, obj);
RegisterEnumerator(cx, last, lastni);
return last;
}
}
if (PropertyIteratorObject* iterobj = LookupInIteratorCache(cx, obj, guards, &key)) {
NativeIterator* ni = iterobj->getNativeIterator();
UpdateNativeIterator(ni, obj);
RegisterEnumerator(cx, iterobj, ni);
return iterobj;
}
if (PropertyIteratorObject* iterObj = LookupInIteratorCache(cx, obj, guards, &key))
return iterObj;
if (!guards.empty() && !CanStoreInIteratorCache(cx, obj))
guards.clear();
}
@ -1057,6 +1015,14 @@ js::GetIterator(JSContext* cx, HandleObject obj, unsigned flags)
return iterobj;
}
PropertyIteratorObject*
js::LookupInIteratorCache(JSContext* cx, HandleObject obj)
{
ReceiverGuardVector guards(cx);
uint32_t key;
return LookupInIteratorCache(cx, obj, guards, &key);
}
// ES 2017 draft 7.4.7.
JSObject*
js::CreateIterResultObject(JSContext* cx, HandleValue value, bool done)

Просмотреть файл

@ -160,6 +160,9 @@ NewStringIteratorObject(JSContext* cx, NewObjectKind newKind = GenericObject);
JSObject*
GetIterator(JSContext* cx, HandleObject obj, unsigned flags);
PropertyIteratorObject*
LookupInIteratorCache(JSContext* cx, HandleObject obj);
/*
* Creates either a key or value iterator, depending on flags. For a value
* iterator, performs value-lookup to convert the given list of jsids.