summaryrefslogtreecommitdiffstats
path: root/js/src/jit
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit')
-rw-r--r--js/src/jit/BaselineBailouts.cpp35
-rw-r--r--js/src/jit/BaselineCompiler.cpp70
-rw-r--r--js/src/jit/BaselineCompiler.h12
-rw-r--r--js/src/jit/CodeGenerator.cpp109
-rw-r--r--js/src/jit/CodeGenerator.h7
-rw-r--r--js/src/jit/IonBuilder.cpp63
-rw-r--r--js/src/jit/IonBuilder.h3
-rw-r--r--js/src/jit/JitFrames.cpp99
-rw-r--r--js/src/jit/Lowering.cpp13
-rw-r--r--js/src/jit/Lowering.h1
-rw-r--r--js/src/jit/MIR.h32
-rw-r--r--js/src/jit/MIRGraph.cpp13
-rw-r--r--js/src/jit/MIRGraph.h3
-rw-r--r--js/src/jit/MOpcodes.h1
-rw-r--r--js/src/jit/VMFunctions.cpp9
-rw-r--r--js/src/jit/VMFunctions.h4
-rw-r--r--js/src/jit/shared/LIR-shared.h21
-rw-r--r--js/src/jit/shared/LOpcodes-shared.h1
18 files changed, 404 insertions, 92 deletions
diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp
index 8fc8a522d..3ab722b3d 100644
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -487,7 +487,7 @@ GetNextNonLoopEntryPc(jsbytecode* pc)
}
static bool
-HasLiveIteratorAtStackDepth(JSScript* script, jsbytecode* pc, uint32_t stackDepth)
+HasLiveStackValueAtDepth(JSScript* script, jsbytecode* pc, uint32_t stackDepth)
{
if (!script->hasTrynotes())
return false;
@@ -501,14 +501,31 @@ HasLiveIteratorAtStackDepth(JSScript* script, jsbytecode* pc, uint32_t stackDept
if (pcOffset >= tn->start + tn->length)
continue;
- // For-in loops have only the iterator on stack.
- if (tn->kind == JSTRY_FOR_IN && stackDepth == tn->stackDepth)
- return true;
+ switch (tn->kind) {
+ case JSTRY_FOR_IN:
+ // For-in loops have only the iterator on stack.
+ if (stackDepth == tn->stackDepth)
+ return true;
+ break;
+
+ case JSTRY_FOR_OF:
+ // For-of loops have the iterator, the result object, and the value
+ // of the result object on stack. The iterator is below the result
+ // object and the value.
+ if (stackDepth == tn->stackDepth - 2)
+ return true;
+ break;
+
+ case JSTRY_DESTRUCTURING_ITERCLOSE:
+ // Destructuring code that need to call IteratorClose have both
+ // the iterator and the "done" value on the stack.
+ if (stackDepth == tn->stackDepth || stackDepth == tn->stackDepth - 1)
+ return true;
+ break;
- // For-of loops have both the iterator and the result object on
- // stack. The iterator is below the result object.
- if (tn->kind == JSTRY_FOR_OF && stackDepth == tn->stackDepth - 1)
- return true;
+ default:
+ break;
+ }
}
return false;
@@ -945,7 +962,7 @@ InitFromBailout(JSContext* cx, HandleScript caller, jsbytecode* callerPC,
// iterators, however, so read them out. They will be closed by
// HandleExceptionBaseline.
MOZ_ASSERT(cx->compartment()->isDebuggee());
- if (iter.moreFrames() || HasLiveIteratorAtStackDepth(script, pc, i + 1)) {
+ if (iter.moreFrames() || HasLiveStackValueAtDepth(script, pc, i + 1)) {
v = iter.read();
} else {
iter.skip();
diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp
index 4dcc10b61..3fa5a80ed 100644
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1063,6 +1063,12 @@ BaselineCompiler::emit_JSOP_NOP_DESTRUCTURING()
}
bool
+BaselineCompiler::emit_JSOP_TRY_DESTRUCTURING_ITERCLOSE()
+{
+ return true;
+}
+
+bool
BaselineCompiler::emit_JSOP_LABEL()
{
return true;
@@ -1147,7 +1153,7 @@ BaselineCompiler::emit_JSOP_PICK()
// after : A B D E C
// First, move value at -(amount + 1) into R0.
- int depth = -(GET_INT8(pc) + 1);
+ int32_t depth = -(GET_INT8(pc) + 1);
masm.loadValue(frame.addressOfStackValue(frame.peek(depth)), R0);
// Move the other values down.
@@ -1166,6 +1172,34 @@ BaselineCompiler::emit_JSOP_PICK()
}
bool
+BaselineCompiler::emit_JSOP_UNPICK()
+{
+ frame.syncStack(0);
+
+ // Pick takes the top of the stack value and moves it under the nth value.
+ // For instance, unpick 2:
+ // before: A B C D E
+ // after : A B E C D
+
+ // First, move value at -1 into R0.
+ masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
+
+ // Move the other values up.
+ int32_t depth = -(GET_INT8(pc) + 1);
+ for (int32_t i = -1; i > depth; i--) {
+ Address source = frame.addressOfStackValue(frame.peek(i - 1));
+ Address dest = frame.addressOfStackValue(frame.peek(i));
+ masm.loadValue(source, R1);
+ masm.storeValue(R1, dest);
+ }
+
+ // Store R0 under the nth value.
+ Address dest = frame.addressOfStackValue(frame.peek(depth));
+ masm.storeValue(R0, dest);
+ return true;
+}
+
+bool
BaselineCompiler::emit_JSOP_GOTO()
{
frame.syncStack(0);
@@ -1353,6 +1387,26 @@ BaselineCompiler::emit_JSOP_CHECKISOBJ()
return true;
}
+typedef bool (*CheckIsCallableFn)(JSContext*, HandleValue, CheckIsCallableKind);
+static const VMFunction CheckIsCallableInfo =
+ FunctionInfo<CheckIsCallableFn>(CheckIsCallable, "CheckIsCallable");
+
+bool
+BaselineCompiler::emit_JSOP_CHECKISCALLABLE()
+{
+ frame.syncStack(0);
+ masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), R0);
+
+ prepareVMCall();
+
+ pushArg(Imm32(GET_UINT8(pc)));
+ pushArg(R0);
+ if (!callVM(CheckIsCallableInfo))
+ return false;
+
+ return true;
+}
+
typedef bool (*ThrowUninitializedThisFn)(JSContext*, BaselineFrame* frame);
static const VMFunction ThrowUninitializedThisInfo =
FunctionInfo<ThrowUninitializedThisFn>(BaselineThrowUninitializedThis,
@@ -3947,7 +4001,7 @@ BaselineCompiler::emit_JSOP_MOREITER()
}
bool
-BaselineCompiler::emit_JSOP_ISNOITER()
+BaselineCompiler::emitIsMagicValue()
{
frame.syncStack(0);
@@ -3966,6 +4020,12 @@ BaselineCompiler::emit_JSOP_ISNOITER()
}
bool
+BaselineCompiler::emit_JSOP_ISNOITER()
+{
+ return emitIsMagicValue();
+}
+
+bool
BaselineCompiler::emit_JSOP_ENDITER()
{
if (!emit_JSOP_JUMPTARGET())
@@ -3977,6 +4037,12 @@ BaselineCompiler::emit_JSOP_ENDITER()
}
bool
+BaselineCompiler::emit_JSOP_ISGENCLOSING()
+{
+ return emitIsMagicValue();
+}
+
+bool
BaselineCompiler::emit_JSOP_GETRVAL()
{
frame.syncStack(0);
diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h
index 77f4dd005..6b5bf009e 100644
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -42,6 +42,7 @@ namespace jit {
_(JSOP_DUP2) \
_(JSOP_SWAP) \
_(JSOP_PICK) \
+ _(JSOP_UNPICK) \
_(JSOP_GOTO) \
_(JSOP_IFEQ) \
_(JSOP_IFNE) \
@@ -202,6 +203,7 @@ namespace jit {
_(JSOP_MOREITER) \
_(JSOP_ISNOITER) \
_(JSOP_ENDITER) \
+ _(JSOP_ISGENCLOSING) \
_(JSOP_GENERATOR) \
_(JSOP_INITIALYIELD) \
_(JSOP_YIELD) \
@@ -216,6 +218,7 @@ namespace jit {
_(JSOP_FUNCTIONTHIS) \
_(JSOP_GLOBALTHIS) \
_(JSOP_CHECKISOBJ) \
+ _(JSOP_CHECKISCALLABLE) \
_(JSOP_CHECKTHIS) \
_(JSOP_CHECKRETURN) \
_(JSOP_NEWTARGET) \
@@ -223,7 +226,7 @@ namespace jit {
_(JSOP_SPREADSUPERCALL) \
_(JSOP_THROWSETCONST) \
_(JSOP_THROWSETALIASEDCONST) \
- _(JSOP_THROWSETCALLEE) \
+ _(JSOP_THROWSETCALLEE) \
_(JSOP_INITHIDDENPROP_GETTER) \
_(JSOP_INITHIDDENPROP_SETTER) \
_(JSOP_INITHIDDENELEM) \
@@ -231,8 +234,9 @@ namespace jit {
_(JSOP_INITHIDDENELEM_SETTER) \
_(JSOP_CHECKOBJCOERCIBLE) \
_(JSOP_DEBUGCHECKSELFHOSTED) \
- _(JSOP_JUMPTARGET) \
- _(JSOP_IS_CONSTRUCTING)
+ _(JSOP_JUMPTARGET) \
+ _(JSOP_IS_CONSTRUCTING) \
+ _(JSOP_TRY_DESTRUCTURING_ITERCLOSE)
class BaselineCompiler : public BaselineCompilerSpecific
{
@@ -341,6 +345,8 @@ class BaselineCompiler : public BaselineCompilerSpecific
MOZ_MUST_USE bool emitThrowConstAssignment();
MOZ_MUST_USE bool emitUninitializedLexicalCheck(const ValueOperand& val);
+ MOZ_MUST_USE bool emitIsMagicValue();
+
MOZ_MUST_USE bool addPCMappingEntry(bool addIndexEntry);
MOZ_MUST_USE bool addYieldOffset();
diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp
index 3b5ec6baa..ccdc5fbfa 100644
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -11326,25 +11326,35 @@ class OutOfLineIsCallable : public OutOfLineCodeBase<CodeGenerator>
}
};
+template <CodeGenerator::CallableOrConstructor mode>
void
-CodeGenerator::visitIsCallable(LIsCallable* ins)
+CodeGenerator::emitIsCallableOrConstructor(Register object, Register output, Label* failure)
{
- Register object = ToRegister(ins->object());
- Register output = ToRegister(ins->output());
-
- OutOfLineIsCallable* ool = new(alloc()) OutOfLineIsCallable(ins);
- addOutOfLineCode(ool, ins->mir());
-
Label notFunction, hasCOps, done;
masm.loadObjClass(object, output);
- // Just skim proxies off. Their notion of isCallable() is more complicated.
- masm.branchTestClassIsProxy(true, output, ool->entry());
+ // Just skim proxies off. Their notion of isCallable()/isConstructor() is
+ // more complicated.
+ masm.branchTestClassIsProxy(true, output, failure);
// An object is callable iff:
// is<JSFunction>() || (getClass()->cOps && getClass()->cOps->call).
+ // An object is constructor iff:
+ // ((is<JSFunction>() && as<JSFunction>().isConstructor) ||
+ // (getClass()->cOps && getClass()->cOps->construct)).
masm.branchPtr(Assembler::NotEqual, output, ImmPtr(&JSFunction::class_), &notFunction);
- masm.move32(Imm32(1), output);
+ if (mode == Callable) {
+ masm.move32(Imm32(1), output);
+ } else {
+ Label notConstructor;
+ masm.load16ZeroExtend(Address(object, JSFunction::offsetOfFlags()), output);
+ masm.and32(Imm32(JSFunction::CONSTRUCTOR), output);
+ masm.branchTest32(Assembler::Zero, output, output, &notConstructor);
+ masm.move32(Imm32(1), output);
+ masm.jump(&done);
+ masm.bind(&notConstructor);
+ masm.move32(Imm32(0), output);
+ }
masm.jump(&done);
masm.bind(&notFunction);
@@ -11355,10 +11365,26 @@ CodeGenerator::visitIsCallable(LIsCallable* ins)
masm.bind(&hasCOps);
masm.loadPtr(Address(output, offsetof(js::Class, cOps)), output);
- masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::ClassOps, call)),
+ size_t opsOffset = mode == Callable
+ ? offsetof(js::ClassOps, call)
+ : offsetof(js::ClassOps, construct);
+ masm.cmpPtrSet(Assembler::NonZero, Address(output, opsOffset),
ImmPtr(nullptr), output);
masm.bind(&done);
+}
+
+void
+CodeGenerator::visitIsCallable(LIsCallable* ins)
+{
+ Register object = ToRegister(ins->object());
+ Register output = ToRegister(ins->output());
+
+ OutOfLineIsCallable* ool = new(alloc()) OutOfLineIsCallable(ins);
+ addOutOfLineCode(ool, ins->mir());
+
+ emitIsCallableOrConstructor<Callable>(object, output, ool->entry());
+
masm.bind(ool->rejoin());
}
@@ -11378,6 +11404,36 @@ CodeGenerator::visitOutOfLineIsCallable(OutOfLineIsCallable* ool)
masm.jump(ool->rejoin());
}
+typedef bool (*CheckIsCallableFn)(JSContext*, HandleValue, CheckIsCallableKind);
+static const VMFunction CheckIsCallableInfo =
+ FunctionInfo<CheckIsCallableFn>(CheckIsCallable, "CheckIsCallable");
+
+void
+CodeGenerator::visitCheckIsCallable(LCheckIsCallable* ins)
+{
+ ValueOperand checkValue = ToValue(ins, LCheckIsCallable::CheckValue);
+ Register temp = ToRegister(ins->temp());
+
+ // OOL code is used in the following 2 cases:
+ // * checkValue is not callable
+ // * checkValue is proxy and it's unknown whether it's callable or not
+ // CheckIsCallable checks if passed value is callable, regardless of the
+ // cases above. IsCallable operation is not observable and checking it
+ // again doesn't matter.
+ OutOfLineCode* ool = oolCallVM(CheckIsCallableInfo, ins,
+ ArgList(checkValue, Imm32(ins->mir()->checkKind())),
+ StoreNothing());
+
+ masm.branchTestObject(Assembler::NotEqual, checkValue, ool->entry());
+
+ Register object = masm.extractObject(checkValue, temp);
+ emitIsCallableOrConstructor<Callable>(object, temp, ool->entry());
+
+ masm.branchTest32(Assembler::Zero, temp, temp, ool->entry());
+
+ masm.bind(ool->rejoin());
+}
+
class OutOfLineIsConstructor : public OutOfLineCodeBase<CodeGenerator>
{
LIsConstructor* ins_;
@@ -11404,37 +11460,8 @@ CodeGenerator::visitIsConstructor(LIsConstructor* ins)
OutOfLineIsConstructor* ool = new(alloc()) OutOfLineIsConstructor(ins);
addOutOfLineCode(ool, ins->mir());
- Label notFunction, notConstructor, hasCOps, done;
- masm.loadObjClass(object, output);
-
- // Just skim proxies off. Their notion of isConstructor() is more complicated.
- masm.branchTestClassIsProxy(true, output, ool->entry());
+ emitIsCallableOrConstructor<Constructor>(object, output, ool->entry());
- // An object is constructor iff
- // ((is<JSFunction>() && as<JSFunction>().isConstructor) ||
- // (getClass()->cOps && getClass()->cOps->construct)).
- masm.branchPtr(Assembler::NotEqual, output, ImmPtr(&JSFunction::class_), &notFunction);
- masm.load16ZeroExtend(Address(object, JSFunction::offsetOfFlags()), output);
- masm.and32(Imm32(JSFunction::CONSTRUCTOR), output);
- masm.branchTest32(Assembler::Zero, output, output, &notConstructor);
- masm.move32(Imm32(1), output);
- masm.jump(&done);
- masm.bind(&notConstructor);
- masm.move32(Imm32(0), output);
- masm.jump(&done);
-
- masm.bind(&notFunction);
- masm.branchPtr(Assembler::NonZero, Address(output, offsetof(js::Class, cOps)),
- ImmPtr(nullptr), &hasCOps);
- masm.move32(Imm32(0), output);
- masm.jump(&done);
-
- masm.bind(&hasCOps);
- masm.loadPtr(Address(output, offsetof(js::Class, cOps)), output);
- masm.cmpPtrSet(Assembler::NonZero, Address(output, offsetof(js::ClassOps, construct)),
- ImmPtr(nullptr), output);
-
- masm.bind(&done);
masm.bind(ool->rejoin());
}
diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h
index b69e919a3..d3126651b 100644
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -364,6 +364,12 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitCallDOMNative(LCallDOMNative* lir);
void visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir);
void visitCallBindVar(LCallBindVar* lir);
+ enum CallableOrConstructor {
+ Callable,
+ Constructor
+ };
+ template <CallableOrConstructor mode>
+ void emitIsCallableOrConstructor(Register object, Register output, Label* failure);
void visitIsCallable(LIsCallable* lir);
void visitOutOfLineIsCallable(OutOfLineIsCallable* ool);
void visitIsConstructor(LIsConstructor* lir);
@@ -384,6 +390,7 @@ class CodeGenerator final : public CodeGeneratorSpecific
void visitArrowNewTarget(LArrowNewTarget* ins);
void visitCheckReturn(LCheckReturn* ins);
void visitCheckIsObj(LCheckIsObj* ins);
+ void visitCheckIsCallable(LCheckIsCallable* ins);
void visitCheckObjCoercible(LCheckObjCoercible* ins);
void visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins);
void visitNaNToZero(LNaNToZero* ins);
diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp
index 2e7784ff4..54d05cac4 100644
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -958,30 +958,35 @@ IonBuilder::build()
bool
IonBuilder::processIterators()
{
- // Find phis that must directly hold an iterator live.
- Vector<MPhi*, 0, SystemAllocPolicy> worklist;
+ // Find and mark phis that must transitively hold an iterator live.
+
+ Vector<MDefinition*, 8, SystemAllocPolicy> worklist;
+
for (size_t i = 0; i < iterators_.length(); i++) {
- MInstruction* ins = iterators_[i];
- for (MUseDefIterator iter(ins); iter; iter++) {
- if (iter.def()->isPhi()) {
- if (!worklist.append(iter.def()->toPhi()))
- return false;
- }
+ MDefinition* iter = iterators_[i];
+ if (!iter->isInWorklist()) {
+ if (!worklist.append(iter))
+ return false;
+ iter->setInWorklist();
}
}
- // Propagate the iterator and live status of phis to all other connected
- // phis.
while (!worklist.empty()) {
- MPhi* phi = worklist.popCopy();
- phi->setIterator();
- phi->setImplicitlyUsedUnchecked();
-
- for (MUseDefIterator iter(phi); iter; iter++) {
- if (iter.def()->isPhi()) {
- MPhi* other = iter.def()->toPhi();
- if (!other->isIterator() && !worklist.append(other))
+ MDefinition* def = worklist.popCopy();
+ def->setNotInWorklist();
+
+ if (def->isPhi()) {
+ MPhi* phi = def->toPhi();
+ phi->setIterator();
+ phi->setImplicitlyUsedUnchecked();
+ }
+
+ for (MUseDefIterator iter(def); iter; iter++) {
+ MDefinition* use = iter.def();
+ if (!use->isInWorklist() && (!use->isPhi() || !use->toPhi()->isIterator())) {
+ if (!worklist.append(use))
return false;
+ use->setInWorklist();
}
}
}
@@ -1563,6 +1568,7 @@ IonBuilder::traverseBytecode()
case JSOP_DUP:
case JSOP_DUP2:
case JSOP_PICK:
+ case JSOP_UNPICK:
case JSOP_SWAP:
case JSOP_SETARG:
case JSOP_SETLOCAL:
@@ -1672,6 +1678,7 @@ IonBuilder::inspectOpcode(JSOp op)
switch (op) {
case JSOP_NOP:
case JSOP_NOP_DESTRUCTURING:
+ case JSOP_TRY_DESTRUCTURING_ITERCLOSE:
case JSOP_LINENO:
case JSOP_LOOPENTRY:
case JSOP_JUMPTARGET:
@@ -1935,6 +1942,10 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_CALLITER:
case JSOP_NEW:
case JSOP_SUPERCALL:
+ if (op == JSOP_CALLITER) {
+ if (!outermostBuilder()->iterators_.append(current->peek(-1)))
+ return false;
+ }
return jsop_call(GET_ARGC(pc), (JSOp)*pc == JSOP_NEW || (JSOp)*pc == JSOP_SUPERCALL);
case JSOP_EVAL:
@@ -2017,6 +2028,10 @@ IonBuilder::inspectOpcode(JSOp op)
current->pick(-GET_INT8(pc));
return true;
+ case JSOP_UNPICK:
+ current->unpick(-GET_INT8(pc));
+ return true;
+
case JSOP_GETALIASEDVAR:
return jsop_getaliasedvar(EnvironmentCoordinate(pc));
@@ -2169,6 +2184,9 @@ IonBuilder::inspectOpcode(JSOp op)
case JSOP_CHECKISOBJ:
return jsop_checkisobj(GET_UINT8(pc));
+ case JSOP_CHECKISCALLABLE:
+ return jsop_checkiscallable(GET_UINT8(pc));
+
case JSOP_CHECKOBJCOERCIBLE:
return jsop_checkobjcoercible();
@@ -10886,6 +10904,15 @@ IonBuilder::jsop_checkisobj(uint8_t kind)
}
bool
+IonBuilder::jsop_checkiscallable(uint8_t kind)
+{
+ MCheckIsCallable* check = MCheckIsCallable::New(alloc(), current->pop(), kind);
+ current->add(check);
+ current->push(check);
+ return true;
+}
+
+bool
IonBuilder::jsop_checkobjcoercible()
{
MDefinition* toCheck = current->peek(-1);
diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h
index 0d1bdb1e3..35ad120f7 100644
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -783,6 +783,7 @@ class IonBuilder
MOZ_MUST_USE bool jsop_debugger();
MOZ_MUST_USE bool jsop_newtarget();
MOZ_MUST_USE bool jsop_checkisobj(uint8_t kind);
+ MOZ_MUST_USE bool jsop_checkiscallable(uint8_t kind);
MOZ_MUST_USE bool jsop_checkobjcoercible();
MOZ_MUST_USE bool jsop_pushcallobj();
@@ -1242,7 +1243,7 @@ class IonBuilder
Vector<ControlFlowInfo, 4, JitAllocPolicy> loops_;
Vector<ControlFlowInfo, 0, JitAllocPolicy> switches_;
Vector<ControlFlowInfo, 2, JitAllocPolicy> labels_;
- Vector<MInstruction*, 2, JitAllocPolicy> iterators_;
+ Vector<MDefinition*, 2, JitAllocPolicy> iterators_;
Vector<LoopHeader, 0, JitAllocPolicy> loopHeaders_;
BaselineInspector* inspector;
diff --git a/js/src/jit/JitFrames.cpp b/js/src/jit/JitFrames.cpp
index 646442b4c..966d952d3 100644
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -328,23 +328,46 @@ NumArgAndLocalSlots(const InlineFrameIterator& frame)
}
static void
-CloseLiveIteratorIon(JSContext* cx, const InlineFrameIterator& frame, uint32_t stackSlot)
+CloseLiveIteratorIon(JSContext* cx, const InlineFrameIterator& frame, JSTryNote* tn)
{
+ MOZ_ASSERT(tn->kind == JSTRY_FOR_IN ||
+ tn->kind == JSTRY_DESTRUCTURING_ITERCLOSE);
+
+ bool isDestructuring = tn->kind == JSTRY_DESTRUCTURING_ITERCLOSE;
+ MOZ_ASSERT_IF(!isDestructuring, tn->stackDepth > 0);
+ MOZ_ASSERT_IF(isDestructuring, tn->stackDepth > 1);
+
SnapshotIterator si = frame.snapshotIterator();
- // Skip stack slots until we reach the iterator object.
- uint32_t skipSlots = NumArgAndLocalSlots(frame) + stackSlot - 1;
+ // Skip stack slots until we reach the iterator object on the stack. For
+ // the destructuring case, we also need to get the "done" value.
+ uint32_t stackSlot = tn->stackDepth;
+ uint32_t adjust = isDestructuring ? 2 : 1;
+ uint32_t skipSlots = NumArgAndLocalSlots(frame) + stackSlot - adjust;
for (unsigned i = 0; i < skipSlots; i++)
si.skip();
Value v = si.read();
- RootedObject obj(cx, &v.toObject());
+ RootedObject iterObject(cx, &v.toObject());
+
+ if (isDestructuring) {
+ RootedValue doneValue(cx, si.read());
+ bool done = ToBoolean(doneValue);
+ // Do not call IteratorClose if the destructuring iterator is already
+ // done.
+ if (done)
+ return;
+ }
- if (cx->isExceptionPending())
- UnwindIteratorForException(cx, obj);
- else
- UnwindIteratorForUncatchableException(cx, obj);
+ if (cx->isExceptionPending()) {
+ if (tn->kind == JSTRY_FOR_IN)
+ UnwindIteratorForException(cx, iterObject);
+ else
+ IteratorCloseForException(cx, iterObject);
+ } else {
+ UnwindIteratorForUncatchableException(cx, iterObject);
+ }
}
class IonFrameStackDepthOp
@@ -413,25 +436,36 @@ HandleExceptionIon(JSContext* cx, const InlineFrameIterator& frame, ResumeFromEx
if (!script->hasTrynotes())
return;
+ bool inForOfIterClose = false;
+
for (TryNoteIterIon tni(cx, frame); !tni.done(); ++tni) {
JSTryNote* tn = *tni;
switch (tn->kind) {
- case JSTRY_FOR_IN: {
- MOZ_ASSERT(JSOp(*(script->main() + tn->start + tn->length)) == JSOP_ENDITER);
- MOZ_ASSERT(tn->stackDepth > 0);
+ case JSTRY_FOR_IN:
+ case JSTRY_DESTRUCTURING_ITERCLOSE:
+ MOZ_ASSERT_IF(tn->kind == JSTRY_FOR_IN,
+ JSOp(*(script->main() + tn->start + tn->length)) == JSOP_ENDITER);
+ CloseLiveIteratorIon(cx, frame, tn);
+ break;
- uint32_t localSlot = tn->stackDepth;
- CloseLiveIteratorIon(cx, frame, localSlot);
+ case JSTRY_FOR_OF_ITERCLOSE:
+ inForOfIterClose = true;
break;
- }
case JSTRY_FOR_OF:
+ inForOfIterClose = false;
+ break;
+
case JSTRY_LOOP:
break;
case JSTRY_CATCH:
if (cx->isExceptionPending()) {
+ // See corresponding comment in ProcessTryNotes.
+ if (inForOfIterClose)
+ break;
+
// Ion can compile try-catch, but bailing out to catch
// exceptions is slow. Reset the warm-up counter so that if we
// catch many exceptions we won't Ion-compile the script.
@@ -562,6 +596,7 @@ ProcessTryNotesBaseline(JSContext* cx, const JitFrameIterator& frame, Environmen
ResumeFromException* rfe, jsbytecode** pc)
{
RootedScript script(cx, frame.baselineFrame()->script());
+ bool inForOfIterClose = false;
for (TryNoteIterBaseline tni(cx, frame.baselineFrame(), *pc); !tni.done(); ++tni) {
JSTryNote* tn = *tni;
@@ -572,7 +607,11 @@ ProcessTryNotesBaseline(JSContext* cx, const JitFrameIterator& frame, Environmen
// If we're closing a legacy generator, we have to skip catch
// blocks.
if (cx->isClosingGenerator())
- continue;
+ break;
+
+ // See corresponding comment in ProcessTryNotes.
+ if (inForOfIterClose)
+ break;
SettleOnTryNote(cx, tn, frame, ei, rfe, pc);
@@ -588,6 +627,10 @@ ProcessTryNotesBaseline(JSContext* cx, const JitFrameIterator& frame, Environmen
}
case JSTRY_FINALLY: {
+ // See corresponding comment in ProcessTryNotes.
+ if (inForOfIterClose)
+ break;
+
SettleOnTryNote(cx, tn, frame, ei, rfe, pc);
rfe->kind = ResumeFromException::RESUME_FINALLY;
rfe->target = script->baselineScript()->nativeCodeForPC(script, *pc);
@@ -602,7 +645,7 @@ ProcessTryNotesBaseline(JSContext* cx, const JitFrameIterator& frame, Environmen
uint8_t* framePointer;
uint8_t* stackPointer;
BaselineFrameAndStackPointersFromTryNote(tn, frame, &framePointer, &stackPointer);
- Value iterValue(*(Value*) stackPointer);
+ Value iterValue(*reinterpret_cast<Value*>(stackPointer));
RootedObject iterObject(cx, &iterValue.toObject());
if (!UnwindIteratorForException(cx, iterObject)) {
// See comment in the JSTRY_FOR_IN case in Interpreter.cpp's
@@ -614,7 +657,31 @@ ProcessTryNotesBaseline(JSContext* cx, const JitFrameIterator& frame, Environmen
break;
}
+ case JSTRY_DESTRUCTURING_ITERCLOSE: {
+ uint8_t* framePointer;
+ uint8_t* stackPointer;
+ BaselineFrameAndStackPointersFromTryNote(tn, frame, &framePointer, &stackPointer);
+ RootedValue doneValue(cx, *(reinterpret_cast<Value*>(stackPointer)));
+ bool done = ToBoolean(doneValue);
+ if (!done) {
+ Value iterValue(*(reinterpret_cast<Value*>(stackPointer) + 1));
+ RootedObject iterObject(cx, &iterValue.toObject());
+ if (!IteratorCloseForException(cx, iterObject)) {
+ SettleOnTryNote(cx, tn, frame, ei, rfe, pc);
+ return false;
+ }
+ }
+ break;
+ }
+
+ case JSTRY_FOR_OF_ITERCLOSE:
+ inForOfIterClose = true;
+ break;
+
case JSTRY_FOR_OF:
+ inForOfIterClose = false;
+ break;
+
case JSTRY_LOOP:
break;
diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp
index a21a529be..7f28a9020 100644
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4689,6 +4689,19 @@ LIRGenerator::visitCheckIsObj(MCheckIsObj* ins)
}
void
+LIRGenerator::visitCheckIsCallable(MCheckIsCallable* ins)
+{
+ MDefinition* checkVal = ins->checkValue();
+ MOZ_ASSERT(checkVal->type() == MIRType::Value);
+
+ LCheckIsCallable* lir = new(alloc()) LCheckIsCallable(useBox(checkVal),
+ temp());
+ redefine(ins, checkVal);
+ add(lir, ins);
+ assignSafepoint(lir, ins);
+}
+
+void
LIRGenerator::visitCheckObjCoercible(MCheckObjCoercible* ins)
{
MDefinition* checkVal = ins->checkValue();
diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h
index 4062c0960..b2805cb7a 100644
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -329,6 +329,7 @@ class LIRGenerator : public LIRGeneratorSpecific
void visitGuardSharedTypedArray(MGuardSharedTypedArray* ins);
void visitCheckReturn(MCheckReturn* ins);
void visitCheckIsObj(MCheckIsObj* ins);
+ void visitCheckIsCallable(MCheckIsCallable* ins);
void visitCheckObjCoercible(MCheckObjCoercible* ins);
void visitDebugCheckSelfHosted(MDebugCheckSelfHosted* ins);
};
diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h
index 3caa7e357..2de91e2df 100644
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -13455,8 +13455,9 @@ class MCheckIsObj
{
uint8_t checkKind_;
- explicit MCheckIsObj(MDefinition* toCheck, uint8_t checkKind)
- : MUnaryInstruction(toCheck), checkKind_(checkKind)
+ MCheckIsObj(MDefinition* toCheck, uint8_t checkKind)
+ : MUnaryInstruction(toCheck),
+ checkKind_(checkKind)
{
setResultType(MIRType::Value);
setResultTypeSet(toCheck->resultTypeSet());
@@ -13475,6 +13476,33 @@ class MCheckIsObj
}
};
+class MCheckIsCallable
+ : public MUnaryInstruction,
+ public BoxInputsPolicy::Data
+{
+ uint8_t checkKind_;
+
+ MCheckIsCallable(MDefinition* toCheck, uint8_t checkKind)
+ : MUnaryInstruction(toCheck),
+ checkKind_(checkKind)
+ {
+ setResultType(MIRType::Value);
+ setResultTypeSet(toCheck->resultTypeSet());
+ setGuard();
+ }
+
+ public:
+ INSTRUCTION_HEADER(CheckIsCallable)
+ TRIVIAL_NEW_WRAPPERS
+ NAMED_OPERANDS((0, checkValue))
+
+ uint8_t checkKind() const { return checkKind_; }
+
+ AliasSet getAliasSet() const override {
+ return AliasSet::None();
+ }
+};
+
class MCheckObjCoercible
: public MUnaryInstruction,
public BoxInputsPolicy::Data
diff --git a/js/src/jit/MIRGraph.cpp b/js/src/jit/MIRGraph.cpp
index 3a363a5bf..d6e0fa8ff 100644
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -790,6 +790,19 @@ MBasicBlock::pick(int32_t depth)
}
void
+MBasicBlock::unpick(int32_t depth)
+{
+ // unpick take the top of the stack element and move it under the depth-th
+ // element;
+ // unpick(-2):
+ // A B C D E
+ // A B C E D [ swapAt(-1) ]
+ // A B E C D [ swapAt(-2) ]
+ for (int32_t n = -1; n >= depth; n--)
+ swapAt(n);
+}
+
+void
MBasicBlock::swapAt(int32_t depth)
{
uint32_t lhsDepth = stackPosition_ + depth - 1;
diff --git a/js/src/jit/MIRGraph.h b/js/src/jit/MIRGraph.h
index b986218f4..705d70fa1 100644
--- a/js/src/jit/MIRGraph.h
+++ b/js/src/jit/MIRGraph.h
@@ -142,6 +142,9 @@ class MBasicBlock : public TempObject, public InlineListNode<MBasicBlock>
// Move the definition to the top of the stack.
void pick(int32_t depth);
+ // Move the top of the stack definition under the depth-th stack value.
+ void unpick(int32_t depth);
+
// Exchange 2 stack slots at the defined depth
void swapAt(int32_t depth);
diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h
index 1a6911247..bb2ab8190 100644
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -285,6 +285,7 @@ namespace jit {
_(ArrowNewTarget) \
_(CheckReturn) \
_(CheckIsObj) \
+ _(CheckIsCallable) \
_(CheckObjCoercible) \
_(DebugCheckSelfHosted) \
_(AsmJSNeg) \
diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp
index 4edbc3c83..77b9e3647 100644
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1349,5 +1349,14 @@ BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue
return GetFunctionThis(cx, frame, res);
}
+bool
+CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind)
+{
+ if (!IsCallable(v))
+ return ThrowCheckIsCallable(cx, kind);
+
+ return true;
+}
+
} // namespace jit
} // namespace js
diff --git a/js/src/jit/VMFunctions.h b/js/src/jit/VMFunctions.h
index f754d58c7..572f05373 100644
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -13,6 +13,7 @@
#include "jit/CompileInfo.h"
#include "jit/JitFrames.h"
+#include "vm/Interpreter.h"
namespace js {
@@ -802,6 +803,9 @@ ThrowObjectCoercible(JSContext* cx, HandleValue v);
MOZ_MUST_USE bool
BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame, MutableHandleValue res);
+MOZ_MUST_USE bool
+CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind);
+
} // namespace jit
} // namespace js
diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h
index f8e0ce9cc..9dcb527c5 100644
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -8893,6 +8893,27 @@ class LCheckIsObj : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 0>
}
};
+class LCheckIsCallable : public LInstructionHelper<BOX_PIECES, BOX_PIECES, 1>
+{
+ public:
+ LIR_HEADER(CheckIsCallable)
+
+ static const size_t CheckValue = 0;
+
+ LCheckIsCallable(const LBoxAllocation& value, const LDefinition& temp) {
+ setBoxOperand(CheckValue, value);
+ setTemp(0, temp);
+ }
+
+ const LDefinition* temp() {
+ return getTemp(0);
+ }
+
+ MCheckIsCallable* mir() const {
+ return mir_->toCheckIsCallable();
+ }
+};
+
class LCheckObjCoercible : public LCallInstructionHelper<BOX_PIECES, BOX_PIECES, 0>
{
public:
diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h
index e57751437..3eea1b449 100644
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -402,6 +402,7 @@
_(ArrowNewTarget) \
_(CheckReturn) \
_(CheckIsObj) \
+ _(CheckIsCallable) \
_(CheckObjCoercible) \
_(DebugCheckSelfHosted) \
_(AsmJSLoadHeap) \