Bug 831585 - Refactor and simplify for-in bytecode and remove cx->iterValue. r=bhackett

This commit is contained in:
Jan de Mooij 2014-09-17 21:07:37 +02:00
Родитель 3145ec0ae0
Коммит d9aad557bd
29 изменённых файлов: 267 добавлений и 465 удалений

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

@ -604,6 +604,8 @@ NonLocalExitScope::prepareForNonLocalJump(StmtInfoBCE *toStmt)
break;
case STMT_FOR_IN_LOOP:
/* The iterator and the current value are on the stack. */
npops += 1;
FLUSH_POPS();
if (!PopIterator(cx, bce))
return false;
@ -718,10 +720,8 @@ PushLoopStatement(BytecodeEmitter *bce, LoopStmtInfo *stmt, StmtType type, ptrdi
int loopSlots;
if (type == STMT_SPREAD)
loopSlots = 3;
else if (type == STMT_FOR_OF_LOOP)
else if (type == STMT_FOR_IN_LOOP || type == STMT_FOR_OF_LOOP)
loopSlots = 2;
else if (type == STMT_FOR_IN_LOOP)
loopSlots = 1;
else
loopSlots = 0;
@ -4883,6 +4883,11 @@ EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
if (Emit2(cx, bce, JSOP_ITER, (uint8_t) pn->pn_iflags) < 0)
return false;
// For-in loops have both the iterator and the value on the stack. Push
// undefined to balance the stack.
if (Emit1(cx, bce, JSOP_UNDEFINED) < 0)
return false;
// Enter the block before the loop body, after evaluating the obj.
// Initialize let bindings with undefined when entering, as the name
// assigned to is a plain assignment.
@ -4917,18 +4922,11 @@ EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
int loopDepth = bce->stackDepth;
#endif
/*
* Emit code to get the next enumeration value and assign it to the
* left hand side.
*/
if (Emit1(cx, bce, JSOP_ITERNEXT) < 0)
return false;
// Emit code to assign the enumeration value to the left hand side, but
// also leave it on the stack.
if (!EmitAssignment(cx, bce, forHead->pn_kid2, JSOP_NOP, nullptr))
return false;
if (Emit1(cx, bce, JSOP_POP) < 0)
return false;
/* The stack should be balanced around the assignment opcode sequence. */
JS_ASSERT(bce->stackDepth == loopDepth);
@ -4948,9 +4946,13 @@ EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
SetJumpOffsetAt(bce, jmp);
if (!EmitLoopEntry(cx, bce, nullptr))
return false;
if (Emit1(cx, bce, JSOP_POP) < 0)
return false;
if (Emit1(cx, bce, JSOP_MOREITER) < 0)
return false;
ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
if (Emit1(cx, bce, JSOP_ISNOITER) < 0)
return false;
ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFEQ, top - bce->offset());
if (beq < 0)
return false;
@ -4962,6 +4964,10 @@ EmitForIn(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t t
if (!PopStatementBCE(cx, bce))
return false;
// Pop the enumeration value.
if (Emit1(cx, bce, JSOP_POP) < 0)
return false;
if (!bce->tryNoteList.append(JSTRY_ITER, bce->stackDepth, top, bce->offset()))
return false;
if (Emit1(cx, bce, JSOP_ENDITER) < 0)

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

@ -3092,16 +3092,21 @@ BaselineCompiler::emit_JSOP_MOREITER()
}
bool
BaselineCompiler::emit_JSOP_ITERNEXT()
BaselineCompiler::emit_JSOP_ISNOITER()
{
frame.syncStack(0);
masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
ICIteratorNext_Fallback::Compiler compiler(cx);
if (!emitOpIC(compiler.getStub(&stubSpace_)))
return false;
Label isMagic, done;
masm.branchTestMagic(Assembler::Equal, frame.addressOfStackValue(frame.peek(-1)),
&isMagic);
masm.moveValue(BooleanValue(false), R0);
masm.jump(&done);
frame.push(R0);
masm.bind(&isMagic);
masm.moveValue(BooleanValue(true), R0);
masm.bind(&done);
frame.push(R0, JSVAL_TYPE_BOOLEAN);
return true;
}

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

@ -170,7 +170,7 @@ namespace jit {
_(JSOP_TABLESWITCH) \
_(JSOP_ITER) \
_(JSOP_MOREITER) \
_(JSOP_ITERNEXT) \
_(JSOP_ISNOITER) \
_(JSOP_ENDITER) \
_(JSOP_CALLEE) \
_(JSOP_SETRVAL) \

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

@ -9844,15 +9844,16 @@ DoIteratorMoreFallback(JSContext *cx, BaselineFrame *frame, ICIteratorMore_Fallb
FallbackICSpew(cx, stub, "IteratorMore");
bool cond;
if (!IteratorMore(cx, iterObj, &cond))
if (!IteratorMore(cx, iterObj, res))
return false;
res.setBoolean(cond);
// Check if debug mode toggling made the stub invalid.
if (stub.invalid())
return true;
if (!res.isMagic(JS_NO_ITER_VALUE) && !res.isString())
stub->setHasNonStringResult();
if (iterObj->is<PropertyIteratorObject>() &&
!stub->hasStub(ICStub::IteratorMore_Native))
{
@ -9906,107 +9907,27 @@ ICIteratorMore_Native::Compiler::generateStubCode(MacroAssembler &masm)
masm.branchTest32(Assembler::NonZero, Address(nativeIterator, offsetof(NativeIterator, flags)),
Imm32(JSITER_FOREACH), &failure);
// Set output to true if props_cursor < props_end.
masm.loadPtr(Address(nativeIterator, offsetof(NativeIterator, props_end)), scratch);
Address cursorAddr = Address(nativeIterator, offsetof(NativeIterator, props_cursor));
masm.cmpPtrSet(Assembler::LessThan, cursorAddr, scratch, scratch);
// If props_cursor < props_end, load the next string and advance the cursor.
// Else, return MagicValue(JS_NO_ITER_VALUE).
Label iterDone;
Address cursorAddr(nativeIterator, offsetof(NativeIterator, props_cursor));
Address cursorEndAddr(nativeIterator, offsetof(NativeIterator, props_end));
masm.loadPtr(cursorAddr, scratch);
masm.branchPtr(Assembler::BelowOrEqual, cursorEndAddr, scratch, &iterDone);
masm.tagValue(JSVAL_TYPE_BOOLEAN, scratch, R0);
EmitReturnFromIC(masm);
// Failure case - jump to next stub
masm.bind(&failure);
EmitStubGuardFailure(masm);
return true;
}
//
// IteratorNext_Fallback
//
static bool
DoIteratorNextFallback(JSContext *cx, BaselineFrame *frame, ICIteratorNext_Fallback *stub_,
HandleValue iterValue, MutableHandleValue res)
{
// This fallback stub may trigger debug mode toggling.
DebugModeOSRVolatileStub<ICIteratorNext_Fallback *> stub(frame, stub_);
FallbackICSpew(cx, stub, "IteratorNext");
RootedObject iteratorObject(cx, &iterValue.toObject());
if (!IteratorNext(cx, iteratorObject, res))
return false;
// Check if debug mode toggling made the stub invalid.
if (stub.invalid())
return true;
if (!res.isString() && !stub->hasNonStringResult())
stub->setHasNonStringResult();
if (iteratorObject->is<PropertyIteratorObject>() &&
!stub->hasStub(ICStub::IteratorNext_Native))
{
ICIteratorNext_Native::Compiler compiler(cx);
ICStub *newStub = compiler.getStub(compiler.getStubSpace(frame->script()));
if (!newStub)
return false;
stub->addNewStub(newStub);
}
return true;
}
typedef bool (*DoIteratorNextFallbackFn)(JSContext *, BaselineFrame *, ICIteratorNext_Fallback *,
HandleValue, MutableHandleValue);
static const VMFunction DoIteratorNextFallbackInfo =
FunctionInfo<DoIteratorNextFallbackFn>(DoIteratorNextFallback);
bool
ICIteratorNext_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
{
EmitRestoreTailCallReg(masm);
masm.pushValue(R0);
masm.push(BaselineStubReg);
masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
return tailCallVM(DoIteratorNextFallbackInfo, masm);
}
//
// IteratorNext_Native
//
bool
ICIteratorNext_Native::Compiler::generateStubCode(MacroAssembler &masm)
{
Label failure;
Register obj = masm.extractObject(R0, ExtractTemp0);
GeneralRegisterSet regs(availableGeneralRegs(1));
Register nativeIterator = regs.takeAny();
Register scratch = regs.takeAny();
masm.branchTestObjClass(Assembler::NotEqual, obj, scratch,
&PropertyIteratorObject::class_, &failure);
masm.loadObjPrivate(obj, JSObject::ITER_CLASS_NFIXED_SLOTS, nativeIterator);
masm.branchTest32(Assembler::NonZero, Address(nativeIterator, offsetof(NativeIterator, flags)),
Imm32(JSITER_FOREACH), &failure);
// Get cursor, next string.
masm.loadPtr(Address(nativeIterator, offsetof(NativeIterator, props_cursor)), scratch);
// Get next string.
masm.loadPtr(Address(scratch, 0), scratch);
// Increase the cursor.
masm.addPtr(Imm32(sizeof(JSString *)),
Address(nativeIterator, offsetof(NativeIterator, props_cursor)));
masm.addPtr(Imm32(sizeof(JSString *)), cursorAddr);
masm.tagValue(JSVAL_TYPE_STRING, scratch, R0);
EmitReturnFromIC(masm);
masm.bind(&iterDone);
masm.moveValue(MagicValue(JS_NO_ITER_VALUE), R0);
EmitReturnFromIC(masm);
// Failure case - jump to next stub
masm.bind(&failure);
EmitStubGuardFailure(masm);

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

@ -438,8 +438,6 @@ class ICEntry
_(IteratorNew_Fallback) \
_(IteratorMore_Fallback) \
_(IteratorMore_Native) \
_(IteratorNext_Fallback) \
_(IteratorNext_Native) \
_(IteratorClose_Fallback) \
\
_(InstanceOf_Fallback) \
@ -6138,6 +6136,14 @@ class ICIteratorMore_Fallback : public ICFallbackStub
return space->allocate<ICIteratorMore_Fallback>(code);
}
void setHasNonStringResult() {
extra_ = 1;
}
bool hasNonStringResult() const {
MOZ_ASSERT(extra_ <= 1);
return extra_;
}
class Compiler : public ICStubCompiler {
protected:
bool generateStubCode(MacroAssembler &masm);
@ -6184,76 +6190,6 @@ class ICIteratorMore_Native : public ICStub
};
};
// IC for getting the next value in an iterator.
class ICIteratorNext_Fallback : public ICFallbackStub
{
friend class ICStubSpace;
explicit ICIteratorNext_Fallback(JitCode *stubCode)
: ICFallbackStub(ICStub::IteratorNext_Fallback, stubCode)
{ }
public:
static inline ICIteratorNext_Fallback *New(ICStubSpace *space, JitCode *code) {
if (!code)
return nullptr;
return space->allocate<ICIteratorNext_Fallback>(code);
}
void setHasNonStringResult() {
JS_ASSERT(extra_ == 0);
extra_ = 1;
}
bool hasNonStringResult() const {
return extra_;
}
class Compiler : public ICStubCompiler {
protected:
bool generateStubCode(MacroAssembler &masm);
public:
explicit Compiler(JSContext *cx)
: ICStubCompiler(cx, ICStub::IteratorNext_Fallback)
{ }
ICStub *getStub(ICStubSpace *space) {
return ICIteratorNext_Fallback::New(space, getStubCode());
}
};
};
// IC for getting the next value in a native iterator.
class ICIteratorNext_Native : public ICStub
{
friend class ICStubSpace;
explicit ICIteratorNext_Native(JitCode *stubCode)
: ICStub(ICStub::IteratorNext_Native, stubCode)
{ }
public:
static inline ICIteratorNext_Native *New(ICStubSpace *space, JitCode *code) {
if (!code)
return nullptr;
return space->allocate<ICIteratorNext_Native>(code);
}
class Compiler : public ICStubCompiler {
protected:
bool generateStubCode(MacroAssembler &masm);
public:
explicit Compiler(JSContext *cx)
: ICStubCompiler(cx, ICStub::IteratorNext_Native)
{ }
ICStub *getStub(ICStubSpace *space) {
return ICIteratorNext_Native::New(space, getStubCode());
}
};
};
// IC for closing an iterator.
class ICIteratorClose_Fallback : public ICFallbackStub
{

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

@ -381,9 +381,9 @@ BaselineInspector::hasSeenAccessedGetter(jsbytecode *pc)
}
bool
BaselineInspector::hasSeenNonStringIterNext(jsbytecode *pc)
BaselineInspector::hasSeenNonStringIterMore(jsbytecode *pc)
{
JS_ASSERT(JSOp(*pc) == JSOP_ITERNEXT);
JS_ASSERT(JSOp(*pc) == JSOP_MOREITER);
if (!hasBaselineScript())
return false;
@ -391,7 +391,7 @@ BaselineInspector::hasSeenNonStringIterNext(jsbytecode *pc)
const ICEntry &entry = icEntryFromPC(pc);
ICStub *stub = entry.fallbackStub();
return stub->toIteratorNext_Fallback()->hasNonStringResult();
return stub->toIteratorMore_Fallback()->hasNonStringResult();
}
bool

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

@ -107,7 +107,7 @@ class BaselineInspector
bool hasSeenNegativeIndexGetElement(jsbytecode *pc);
bool hasSeenAccessedGetter(jsbytecode *pc);
bool hasSeenDoubleResult(jsbytecode *pc);
bool hasSeenNonStringIterNext(jsbytecode *pc);
bool hasSeenNonStringIterMore(jsbytecode *pc);
JSObject *getTemplateObject(jsbytecode *pc);
JSObject *getTemplateObjectForNative(jsbytecode *pc, Native native);

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

@ -53,8 +53,10 @@ BytecodeAnalysis::init(TempAllocator &alloc, GSNCache &gsn)
Vector<CatchFinallyRange, 0, IonAllocPolicy> catchFinallyRanges(alloc);
for (jsbytecode *pc = script_->code(); pc < end; pc += GetBytecodeLength(pc)) {
jsbytecode *nextpc;
for (jsbytecode *pc = script_->code(); pc < end; pc = nextpc) {
JSOp op = JSOp(*pc);
nextpc = pc + GetBytecodeLength(pc);
unsigned offset = script_->pcToOffset(pc);
JitSpew(JitSpew_BaselineOp, "Analyzing op @ %d (end=%d): %s",
@ -64,7 +66,6 @@ BytecodeAnalysis::init(TempAllocator &alloc, GSNCache &gsn)
if (!infos_[offset].initialized)
continue;
unsigned stackDepth = infos_[offset].stackDepth;
#ifdef DEBUG
for (jsbytecode *chkpc = pc + 1; chkpc < (pc + GetBytecodeLength(pc)); chkpc++)
@ -190,25 +191,20 @@ BytecodeAnalysis::init(TempAllocator &alloc, GSNCache &gsn)
infos_[targetOffset].jumpTarget = true;
if (jumpBack)
pc = script_->offsetToPC(targetOffset);
nextpc = script_->offsetToPC(targetOffset);
}
// Handle any fallthrough from this opcode.
if (BytecodeFallsThrough(op)) {
jsbytecode *nextpc = pc + GetBytecodeLength(pc);
JS_ASSERT(nextpc < end);
unsigned nextOffset = script_->pcToOffset(nextpc);
jsbytecode *fallthrough = pc + GetBytecodeLength(pc);
JS_ASSERT(fallthrough < end);
unsigned fallthroughOffset = script_->pcToOffset(fallthrough);
infos_[nextOffset].init(stackDepth);
if (jump)
infos_[nextOffset].jumpFallthrough = true;
infos_[fallthroughOffset].init(stackDepth);
// Treat the fallthrough of a branch instruction as a jump target.
if (jump)
infos_[nextOffset].jumpTarget = true;
else
infos_[nextOffset].fallthrough = true;
infos_[fallthroughOffset].jumpTarget = true;
}
}

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

@ -21,8 +21,6 @@ struct BytecodeInfo
uint16_t stackDepth;
bool initialized : 1;
bool jumpTarget : 1;
bool jumpFallthrough : 1;
bool fallthrough : 1;
// If true, this is a JSOP_LOOPENTRY op inside a catch or finally block.
bool loopEntryInCatchOrFinally : 1;

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

@ -6369,66 +6369,64 @@ LoadNativeIterator(MacroAssembler &masm, Register obj, Register dest, Label *fai
masm.loadObjPrivate(obj, JSObject::ITER_CLASS_NFIXED_SLOTS, dest);
}
typedef bool (*IteratorNextFn)(JSContext *, HandleObject, MutableHandleValue);
static const VMFunction IteratorNextInfo = FunctionInfo<IteratorNextFn>(IteratorNext);
bool
CodeGenerator::visitIteratorNext(LIteratorNext *lir)
{
const Register obj = ToRegister(lir->object());
const Register temp = ToRegister(lir->temp());
const ValueOperand output = ToOutValue(lir);
OutOfLineCode *ool = oolCallVM(IteratorNextInfo, lir, (ArgList(), obj), StoreValueTo(output));
if (!ool)
return false;
LoadNativeIterator(masm, obj, temp, ool->entry());
masm.branchTest32(Assembler::NonZero, Address(temp, offsetof(NativeIterator, flags)),
Imm32(JSITER_FOREACH), ool->entry());
// Get cursor, next string.
masm.loadPtr(Address(temp, offsetof(NativeIterator, props_cursor)), output.scratchReg());
masm.loadPtr(Address(output.scratchReg(), 0), output.scratchReg());
masm.tagValue(JSVAL_TYPE_STRING, output.scratchReg(), output);
// Increase the cursor.
masm.addPtr(Imm32(sizeof(JSString *)), Address(temp, offsetof(NativeIterator, props_cursor)));
masm.bind(ool->rejoin());
return true;
}
typedef bool (*IteratorMoreFn)(JSContext *, HandleObject, bool *);
typedef bool (*IteratorMoreFn)(JSContext *, HandleObject, MutableHandleValue);
static const VMFunction IteratorMoreInfo = FunctionInfo<IteratorMoreFn>(IteratorMore);
bool
CodeGenerator::visitIteratorMore(LIteratorMore *lir)
{
const Register obj = ToRegister(lir->object());
const Register output = ToRegister(lir->output());
const ValueOperand output = ToOutValue(lir);
const Register temp = ToRegister(lir->temp());
OutOfLineCode *ool = oolCallVM(IteratorMoreInfo, lir,
(ArgList(), obj), StoreRegisterTo(output));
OutOfLineCode *ool = oolCallVM(IteratorMoreInfo, lir, (ArgList(), obj), StoreValueTo(output));
if (!ool)
return false;
LoadNativeIterator(masm, obj, output, ool->entry());
Register outputScratch = output.scratchReg();
LoadNativeIterator(masm, obj, outputScratch, ool->entry());
masm.branchTest32(Assembler::NonZero, Address(output, offsetof(NativeIterator, flags)),
masm.branchTest32(Assembler::NonZero, Address(outputScratch, offsetof(NativeIterator, flags)),
Imm32(JSITER_FOREACH), ool->entry());
// Set output to true if props_cursor < props_end.
masm.loadPtr(Address(output, offsetof(NativeIterator, props_end)), temp);
masm.cmpPtrSet(Assembler::LessThan, Address(output, offsetof(NativeIterator, props_cursor)),
temp, output);
// If props_cursor < props_end, load the next string and advance the cursor.
// Else, return MagicValue(JS_NO_ITER_VALUE).
Label iterDone;
Address cursorAddr(outputScratch, offsetof(NativeIterator, props_cursor));
Address cursorEndAddr(outputScratch, offsetof(NativeIterator, props_end));
masm.loadPtr(cursorAddr, temp);
masm.branchPtr(Assembler::BelowOrEqual, cursorEndAddr, temp, &iterDone);
// Get next string.
masm.loadPtr(Address(temp, 0), temp);
// Increase the cursor.
masm.addPtr(Imm32(sizeof(JSString *)), cursorAddr);
masm.tagValue(JSVAL_TYPE_STRING, temp, output);
masm.jump(ool->rejoin());
masm.bind(&iterDone);
masm.moveValue(MagicValue(JS_NO_ITER_VALUE), output);
masm.bind(ool->rejoin());
return true;
}
bool
CodeGenerator::visitIsNoIterAndBranch(LIsNoIterAndBranch *lir)
{
ValueOperand input = ToValue(lir, LIsNoIterAndBranch::Input);
Label *ifTrue = getJumpLabelForBranch(lir->ifTrue());
Label *ifFalse = getJumpLabelForBranch(lir->ifFalse());
masm.branchTestMagic(Assembler::Equal, input, ifTrue);
if (!isNextBlock(lir->ifFalse()->lir()))
masm.jump(ifFalse);
return true;
}
typedef bool (*CloseIteratorFn)(JSContext *, HandleObject);
static const VMFunction CloseIteratorInfo = FunctionInfo<CloseIteratorFn>(CloseIterator);

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

@ -265,8 +265,8 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitClampVToUint8(LClampVToUint8 *lir);
bool visitCallIteratorStart(LCallIteratorStart *lir);
bool visitIteratorStart(LIteratorStart *lir);
bool visitIteratorNext(LIteratorNext *lir);
bool visitIteratorMore(LIteratorMore *lir);
bool visitIsNoIterAndBranch(LIsNoIterAndBranch *lir);
bool visitIteratorEnd(LIteratorEnd *lir);
bool visitArgumentsLength(LArgumentsLength *lir);
bool visitGetFrameArgument(LGetFrameArgument *lir);

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

@ -491,6 +491,8 @@ IonBuilder::analyzeNewLoopTypes(MBasicBlock *entry, jsbytecode *start, jsbytecod
continue;
if (!analysis().maybeInfo(pc))
continue;
if (!last)
continue;
MPhi *phi = entry->getSlot(slot)->toPhi();
@ -561,7 +563,6 @@ IonBuilder::analyzeNewLoopTypes(MBasicBlock *entry, jsbytecode *start, jsbytecod
case JSOP_STRING:
case JSOP_TYPEOF:
case JSOP_TYPEOFEXPR:
case JSOP_ITERNEXT:
type = MIRType_String;
break;
case JSOP_ADD:
@ -1783,12 +1784,12 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_ITER:
return jsop_iter(GET_INT8(pc));
case JSOP_ITERNEXT:
return jsop_iternext();
case JSOP_MOREITER:
return jsop_itermore();
case JSOP_ISNOITER:
return jsop_isnoiter();
case JSOP_ENDITER:
return jsop_iterend();
@ -2258,6 +2259,23 @@ IonBuilder::processWhileCondEnd(CFGState &state)
pc = state.loop.bodyStart;
if (!setCurrentAndSpecializePhis(body))
return ControlStatus_Error;
// If this is a for-in loop, unbox the current value as string if possible.
if (ins->isIsNoIter()) {
MIteratorMore *iterMore = ins->toIsNoIter()->input()->toIteratorMore();
jsbytecode *iterMorePc = iterMore->resumePoint()->pc();
MOZ_ASSERT(*iterMorePc == JSOP_MOREITER);
if (!nonStringIteration_ && !inspector->hasSeenNonStringIterMore(iterMorePc)) {
MDefinition *val = current->peek(-1);
MOZ_ASSERT(val == iterMore);
MInstruction *ins = MUnbox::New(alloc(), val, MIRType_String, MUnbox::Fallible,
Bailout_NonStringInputInvalidate);
current->add(ins);
current->rewriteAtDepth(-1, ins);
}
}
return ControlStatus_Jumped;
}
@ -10453,28 +10471,6 @@ IonBuilder::jsop_iter(uint8_t flags)
return resumeAfter(ins);
}
bool
IonBuilder::jsop_iternext()
{
MDefinition *iter = current->peek(-1);
MInstruction *ins = MIteratorNext::New(alloc(), iter);
current->add(ins);
current->push(ins);
if (!resumeAfter(ins))
return false;
if (!nonStringIteration_ && !inspector->hasSeenNonStringIterNext(pc)) {
ins = MUnbox::New(alloc(), ins, MIRType_String, MUnbox::Fallible,
Bailout_NonStringInputInvalidate);
current->add(ins);
current->rewriteAtDepth(-1, ins);
}
return true;
}
bool
IonBuilder::jsop_itermore()
{
@ -10487,6 +10483,19 @@ IonBuilder::jsop_itermore()
return resumeAfter(ins);
}
bool
IonBuilder::jsop_isnoiter()
{
MDefinition *def = current->peek(-1);
MOZ_ASSERT(def->isIteratorMore());
MInstruction *ins = MIsNoIter::New(alloc(), def);
current->add(ins);
current->push(ins);
return true;
}
bool
IonBuilder::jsop_iterend()
{

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

@ -651,8 +651,8 @@ class IonBuilder
bool jsop_typeof();
bool jsop_toid();
bool jsop_iter(uint8_t flags);
bool jsop_iternext();
bool jsop_itermore();
bool jsop_isnoiter();
bool jsop_iterend();
bool jsop_in();
bool jsop_in_dense();

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

@ -5571,27 +5571,7 @@ class LIteratorStart : public LInstructionHelper<1, 1, 3>
}
};
class LIteratorNext : public LInstructionHelper<BOX_PIECES, 1, 1>
{
public:
LIR_HEADER(IteratorNext)
LIteratorNext(const LAllocation &iterator, const LDefinition &temp) {
setOperand(0, iterator);
setTemp(0, temp);
}
const LAllocation *object() {
return getOperand(0);
}
const LDefinition *temp() {
return getTemp(0);
}
MIteratorNext *mir() const {
return mir_->toIteratorNext();
}
};
class LIteratorMore : public LInstructionHelper<1, 1, 1>
class LIteratorMore : public LInstructionHelper<BOX_PIECES, 1, 1>
{
public:
LIR_HEADER(IteratorMore)
@ -5611,6 +5591,26 @@ class LIteratorMore : public LInstructionHelper<1, 1, 1>
}
};
class LIsNoIterAndBranch : public LControlInstructionHelper<2, BOX_PIECES, 0>
{
public:
LIR_HEADER(IsNoIterAndBranch)
LIsNoIterAndBranch(MBasicBlock *ifTrue, MBasicBlock *ifFalse) {
setSuccessor(0, ifTrue);
setSuccessor(1, ifFalse);
}
static const size_t Input = 0;
MBasicBlock *ifTrue() const {
return getSuccessor(0);
}
MBasicBlock *ifFalse() const {
return getSuccessor(1);
}
};
class LIteratorEnd : public LInstructionHelper<0, 1, 3>
{
public:

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

@ -259,8 +259,8 @@
_(SetPropertyPolymorphicT) \
_(CallIteratorStart) \
_(IteratorStart) \
_(IteratorNext) \
_(IteratorMore) \
_(IsNoIterAndBranch) \
_(IteratorEnd) \
_(ArrayLength) \
_(SetArrayLength) \

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

@ -874,6 +874,18 @@ LIRGenerator::visitTest(MTest *test)
}
}
if (opd->isIsNoIter()) {
MOZ_ASSERT(opd->isEmittedAtUses());
MDefinition *input = opd->toIsNoIter()->input();
MOZ_ASSERT(input->type() == MIRType_Value);
LIsNoIterAndBranch *lir = new(alloc()) LIsNoIterAndBranch(ifTrue, ifFalse);
if (!useBox(lir, LIsNoIterAndBranch::Input, input))
return false;
return add(lir, test);
}
if (opd->type() == MIRType_Double)
return add(new(alloc()) LTestDAndBranch(useRegister(opd), ifTrue, ifFalse));
@ -3332,17 +3344,17 @@ LIRGenerator::visitIteratorStart(MIteratorStart *ins)
}
bool
LIRGenerator::visitIteratorNext(MIteratorNext *ins)
LIRGenerator::visitIteratorMore(MIteratorMore *ins)
{
LIteratorNext *lir = new(alloc()) LIteratorNext(useRegister(ins->iterator()), temp());
LIteratorMore *lir = new(alloc()) LIteratorMore(useRegister(ins->iterator()), temp());
return defineBox(lir, ins) && assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitIteratorMore(MIteratorMore *ins)
LIRGenerator::visitIsNoIter(MIsNoIter *ins)
{
LIteratorMore *lir = new(alloc()) LIteratorMore(useRegister(ins->iterator()), temp());
return define(lir, ins) && assignSafepoint(lir, ins);
MOZ_ASSERT(ins->hasOneUse());
return emitAtUses(ins);
}
bool

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

@ -236,8 +236,8 @@ class LIRGenerator : public LIRGeneratorSpecific
bool visitSetElementCache(MSetElementCache *ins);
bool visitCallSetProperty(MCallSetProperty *ins);
bool visitIteratorStart(MIteratorStart *ins);
bool visitIteratorNext(MIteratorNext *ins);
bool visitIteratorMore(MIteratorMore *ins);
bool visitIsNoIter(MIsNoIter *ins);
bool visitIteratorEnd(MIteratorEnd *ins);
bool visitStringLength(MStringLength *ins);
bool visitArgumentsLength(MArgumentsLength *ins);

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

@ -10213,31 +10213,6 @@ class MIteratorStart
}
};
class MIteratorNext
: public MUnaryInstruction,
public SingleObjectPolicy
{
explicit MIteratorNext(MDefinition *iter)
: MUnaryInstruction(iter)
{
setResultType(MIRType_Value);
}
public:
INSTRUCTION_HEADER(IteratorNext)
static MIteratorNext *New(TempAllocator &alloc, MDefinition *iter) {
return new(alloc) MIteratorNext(iter);
}
TypePolicy *typePolicy() {
return this;
}
MDefinition *iterator() const {
return getOperand(0);
}
};
class MIteratorMore
: public MUnaryInstruction,
public SingleObjectPolicy
@ -10245,7 +10220,7 @@ class MIteratorMore
explicit MIteratorMore(MDefinition *iter)
: MUnaryInstruction(iter)
{
setResultType(MIRType_Boolean);
setResultType(MIRType_Value);
}
public:
@ -10263,6 +10238,28 @@ class MIteratorMore
}
};
class MIsNoIter
: public MUnaryInstruction
{
MIsNoIter(MDefinition *def)
: MUnaryInstruction(def)
{
setResultType(MIRType_Boolean);
setMovable();
}
public:
INSTRUCTION_HEADER(IsNoIter)
static MIsNoIter *New(TempAllocator &alloc, MDefinition *def) {
return new(alloc) MIsNoIter(def);
}
AliasSet getAliasSet() const {
return AliasSet::None();
}
};
class MIteratorEnd
: public MUnaryInstruction,
public SingleObjectPolicy

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

@ -192,8 +192,8 @@ namespace jit {
_(DeleteElement) \
_(SetPropertyCache) \
_(IteratorStart) \
_(IteratorNext) \
_(IteratorMore) \
_(IsNoIter) \
_(IteratorEnd) \
_(StringLength) \
_(ArgumentsLength) \

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

@ -274,8 +274,8 @@ class ParallelSafetyVisitor : public MDefinitionVisitor
UNSAFE_OP(DeleteElement)
WRITE_GUARDED_OP(SetPropertyCache, object)
UNSAFE_OP(IteratorStart)
UNSAFE_OP(IteratorNext)
UNSAFE_OP(IteratorMore)
UNSAFE_OP(IsNoIter)
UNSAFE_OP(IteratorEnd)
SAFE_OP(StringLength)
SAFE_OP(ArgumentsLength)

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

@ -2062,6 +2062,12 @@ RangeAnalysis::addRangeAssertions()
continue;
}
// MIsNoIter is fused with the MTest that follows it and emitted as
// LIsNoIterAndBranch. Skip it to avoid complicating MIsNoIter
// lowering.
if (ins->isIsNoIter())
continue;
Range r(ins);
// Don't insert assertions if there's nothing interesting to assert.

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

@ -1109,7 +1109,6 @@ JSContext::JSContext(JSRuntime *rt)
data(nullptr),
data2(nullptr),
outstandingRequests(0),
iterValue(MagicValue(JS_NO_ITER_VALUE)),
jitIsBroken(false),
#ifdef MOZ_TRACE_JSCALLS
functionCallback(nullptr),
@ -1304,8 +1303,6 @@ JSContext::mark(JSTracer *trc)
MarkValueRoot(trc, &unwrappedException_, "unwrapped exception");
TraceCycleDetectionSet(trc, cycleDetectorSet);
MarkValueRoot(trc, &iterValue, "iterValue");
}
void *

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

@ -490,9 +490,6 @@ struct JSContext : public js::ExclusiveContext,
without the corresponding
JS_EndRequest. */
/* Location to stash the iteration value between JSOP_MOREITER and JSOP_ITERNEXT. */
js::Value iterValue;
bool jitIsBroken;
void updateJITEnabled();

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

@ -865,16 +865,15 @@ iterator_next_impl(JSContext *cx, CallArgs args)
RootedObject thisObj(cx, &args.thisv().toObject());
bool more;
if (!IteratorMore(cx, thisObj, &more))
if (!IteratorMore(cx, thisObj, args.rval()))
return false;
if (!more) {
if (args.rval().isMagic(JS_NO_ITER_VALUE)) {
ThrowStopIteration(cx);
return false;
}
return IteratorNext(cx, thisObj, args.rval());
return true;
}
static bool
@ -1017,13 +1016,6 @@ js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp)
/* JSITER_KEYVALUE must always come with JSITER_FOREACH */
JS_ASSERT_IF(flags & JSITER_KEYVALUE, flags & JSITER_FOREACH);
/*
* Make sure the more/next state machine doesn't get stuck. A value might
* be left in iterValue when a trace is left due to an interrupt after
* JSOP_MOREITER but before the value is picked up by FOR*.
*/
cx->iterValue.setMagic(JS_NO_ITER_VALUE);
RootedObject obj(cx);
if (vp.isObject()) {
/* Common case. */
@ -1047,8 +1039,6 @@ js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp)
bool
js::CloseIterator(JSContext *cx, HandleObject obj)
{
cx->iterValue.setMagic(JS_NO_ITER_VALUE);
if (obj->is<PropertyIteratorObject>()) {
/* Remove enumerators from the active list, which is a stack. */
NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator();
@ -1255,31 +1245,28 @@ js::SuppressDeletedElements(JSContext *cx, HandleObject obj, uint32_t begin, uin
}
bool
js::IteratorMore(JSContext *cx, HandleObject iterobj, bool *res)
js::IteratorMore(JSContext *cx, HandleObject iterobj, MutableHandleValue rval)
{
/* Fast path for native iterators */
NativeIterator *ni = nullptr;
if (iterobj->is<PropertyIteratorObject>()) {
/* Key iterators are handled by fast-paths. */
ni = iterobj->as<PropertyIteratorObject>().getNativeIterator();
bool more = ni->props_cursor < ni->props_end;
if (ni->isKeyIter() || !more) {
*res = more;
if (ni->props_cursor >= ni->props_end) {
rval.setMagic(JS_NO_ITER_VALUE);
return true;
}
if (ni->isKeyIter()) {
rval.setString(*ni->current());
ni->incCursor();
return true;
}
}
/* We might still have a pending value. */
if (!cx->iterValue.isMagic(JS_NO_ITER_VALUE)) {
*res = true;
return true;
}
/* We're reentering below and can call anything. */
JS_CHECK_RECURSION(cx, return false);
/* Fetch and cache the next value from the iterator. */
RootedValue val(cx);
if (ni) {
JS_ASSERT(!ni->isKeyIter());
RootedId id(cx);
@ -1288,60 +1275,31 @@ js::IteratorMore(JSContext *cx, HandleObject iterobj, bool *res)
return false;
ni->incCursor();
RootedObject obj(cx, ni->obj);
if (!JSObject::getGeneric(cx, obj, obj, id, &val))
if (!JSObject::getGeneric(cx, obj, obj, id, rval))
return false;
if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, val, &val))
if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, rval, rval))
return false;
} else {
/* Call the iterator object's .next method. */
if (!JSObject::getProperty(cx, iterobj, iterobj, cx->names().next, &val))
return false;
if (!Invoke(cx, ObjectValue(*iterobj), val, 0, nullptr, &val)) {
/* Check for StopIteration. */
if (!cx->isExceptionPending())
return false;
RootedValue exception(cx);
if (!cx->getPendingException(&exception))
return false;
if (!JS_IsStopIteration(exception))
return false;
cx->clearPendingException();
cx->iterValue.setMagic(JS_NO_ITER_VALUE);
*res = false;
return true;
}
return true;
}
/* Cache the value returned by iterobj.next() so js_IteratorNext() can find it. */
JS_ASSERT(!val.isMagic(JS_NO_ITER_VALUE));
cx->iterValue = val;
*res = true;
return true;
}
/* Call the iterator object's .next method. */
if (!JSObject::getProperty(cx, iterobj, iterobj, cx->names().next, rval))
return false;
if (!Invoke(cx, ObjectValue(*iterobj), rval, 0, nullptr, rval)) {
/* Check for StopIteration. */
if (!cx->isExceptionPending())
return false;
RootedValue exception(cx);
if (!cx->getPendingException(&exception))
return false;
if (!JS_IsStopIteration(exception))
return false;
bool
js::IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval)
{
/* Fast path for native iterators */
if (iterobj->is<PropertyIteratorObject>()) {
/*
* Implement next directly as all the methods of the native iterator are
* read-only and permanent.
*/
NativeIterator *ni = iterobj->as<PropertyIteratorObject>().getNativeIterator();
if (ni->isKeyIter()) {
JS_ASSERT(ni->props_cursor < ni->props_end);
rval.setString(*ni->current());
ni->incCursor();
return true;
}
cx->clearPendingException();
rval.setMagic(JS_NO_ITER_VALUE);
return true;
}
JS_ASSERT(!cx->iterValue.isMagic(JS_NO_ITER_VALUE));
rval.set(cx->iterValue);
cx->iterValue.setMagic(JS_NO_ITER_VALUE);
return true;
}

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

@ -201,15 +201,11 @@ extern bool
SuppressDeletedElements(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end);
/*
* IteratorMore() indicates whether another value is available. It might
* internally call iterobj.next() and then cache the value until its
* picked up by IteratorNext(). The value is cached in the current context.
* IteratorMore() returns the next iteration value. If no value is available,
* MagicValue(JS_NO_ITER_VALUE) is returned.
*/
extern bool
IteratorMore(JSContext *cx, HandleObject iterobj, bool *res);
extern bool
IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval);
IteratorMore(JSContext *cx, HandleObject iterobj, MutableHandleValue rval);
extern bool
ThrowStopIteration(JSContext *cx);

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

@ -1340,21 +1340,6 @@ Debugger::onSingleStep(JSContext *cx, MutableHandleValue vp)
}
#endif
/* Preserve the debuggee's iterValue while handlers run. */
class PreserveIterValue {
JSContext *cx;
RootedValue savedIterValue;
public:
explicit PreserveIterValue(JSContext *cx) : cx(cx), savedIterValue(cx, cx->iterValue) {
cx->iterValue.setMagic(JS_NO_ITER_VALUE);
}
~PreserveIterValue() {
cx->iterValue = savedIterValue;
}
};
PreserveIterValue piv(cx);
/* Call all the onStep handlers we found. */
for (JSObject **p = frames.begin(); p != frames.end(); p++) {
RootedObject frame(cx, *p);

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

@ -455,9 +455,6 @@ js::Invoke(JSContext *cx, CallArgs args, MaybeConstruct construct)
JS_ASSERT(args.length() <= ARGS_LENGTH_MAX);
JS_ASSERT(!cx->compartment()->activeAnalysis);
/* We should never enter a new script while cx->iterValue is live. */
JS_ASSERT(cx->iterValue.isMagic(JS_NO_ITER_VALUE));
/* Perform GC if necessary on exit from the function. */
AutoGCIfNeeded gcIfNeeded(cx);
@ -1931,24 +1928,17 @@ CASE(JSOP_MOREITER)
PUSH_NULL();
RootedObject &obj = rootObject0;
obj = &REGS.sp[-2].toObject();
bool cond;
if (!IteratorMore(cx, obj, &cond))
if (!IteratorMore(cx, obj, REGS.stackHandleAt(-1)))
goto error;
REGS.sp[-1].setBoolean(cond);
}
END_CASE(JSOP_MOREITER)
CASE(JSOP_ITERNEXT)
CASE(JSOP_ISNOITER)
{
JS_ASSERT(REGS.sp[-1].isObject());
PUSH_NULL();
MutableHandleValue res = REGS.stackHandleAt(-1);
RootedObject &obj = rootObject0;
obj = &REGS.sp[-2].toObject();
if (!IteratorNext(cx, obj, res))
goto error;
bool b = REGS.sp[-1].isMagic(JS_NO_ITER_VALUE);
PUSH_BOOLEAN(b);
}
END_CASE(JSOP_ITERNEXT)
END_CASE(JSOP_ISNOITER)
CASE(JSOP_ENDITER)
{

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

@ -615,30 +615,25 @@
*/ \
macro(JSOP_ITER, 75, "iter", NULL, 2, 1, 1, JOF_UINT8) \
/*
* Stores the next iterated value into 'cx->iterValue' and pushes 'true'
* onto the stack if another value is available, and 'false' otherwise.
* It is followed immediately by JSOP_IFNE.
* Pushes the next iterated value onto the stack. If no value is available,
* MagicValue(JS_NO_ITER_VALUE) is pushed.
*
* This opcode increments iterator cursor if current iteration has
* JSITER_FOREACH flag.
* Category: Statements
* Type: For-In Statement
* Operands:
* Stack: iter => iter, cond
*/ \
macro(JSOP_MOREITER, 76, "moreiter", NULL, 1, 1, 2, JOF_BYTE) \
/*
* Pushes the value produced by the preceding JSOP_MOREITER operation
* ('cx->iterValue') onto the stack
*
* This opcode increments iterator cursor if current iteration does not have
* JSITER_FOREACH flag.
* Category: Statements
* Type: For-In Statement
* Operands:
* Stack: iter => iter, val
*/ \
macro(JSOP_ITERNEXT, 77, "iternext", "<next>", 1, 0, 1, JOF_BYTE) \
macro(JSOP_MOREITER, 76, "moreiter", NULL, 1, 1, 2, JOF_BYTE) \
/*
* Pushes a boolean indicating whether the value on top of the stack is
* MagicValue(JS_NO_ITER_VALUE).
*
* Category: Statements
* Type: For-In Statement
* Operands:
* Stack: val => val, res
*/ \
macro(JSOP_ISNOITER, 77, "isnoiter", NULL, 1, 1, 2, JOF_BYTE) \
/*
* Exits a for-in loop by popping the iterator object from the stack and
* closing it.

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

@ -28,7 +28,7 @@ namespace js {
*
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
*/
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 183);
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 184);
class XDRBuffer {
public: