зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1093573 part 10 - Baseline-compile JSOP_RESUME. r=shu,wingo
This commit is contained in:
Родитель
3c38cb2380
Коммит
e12021be76
|
@ -3354,3 +3354,230 @@ BaselineCompiler::emit_JSOP_FINALYIELDRVAL()
|
|||
masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
|
||||
return emitReturn();
|
||||
}
|
||||
|
||||
typedef bool (*InterpretResumeFn)(JSContext *, HandleObject, HandleValue, HandlePropertyName,
|
||||
MutableHandleValue);
|
||||
static const VMFunction InterpretResumeInfo = FunctionInfo<InterpretResumeFn>(jit::InterpretResume);
|
||||
|
||||
typedef bool (*GeneratorThrowFn)(JSContext *, HandleObject, HandleValue);
|
||||
static const VMFunction GeneratorThrowInfo = FunctionInfo<GeneratorThrowFn>(js::GeneratorThrow);
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_RESUME()
|
||||
{
|
||||
GeneratorObject::ResumeKind resumeKind = GeneratorObject::getResumeKind(pc);
|
||||
|
||||
frame.syncStack(0);
|
||||
masm.checkStackAlignment();
|
||||
|
||||
GeneralRegisterSet regs(GeneralRegisterSet::All());
|
||||
regs.take(BaselineFrameReg);
|
||||
|
||||
// Load generator object.
|
||||
Register genObj = regs.takeAny();
|
||||
masm.unboxObject(frame.addressOfStackValue(frame.peek(-2)), genObj);
|
||||
|
||||
if (resumeKind == GeneratorObject::CLOSE) {
|
||||
// Resume the CLOSE operation in the interpreter. This is only used for
|
||||
// legacy generators and requires some complicated exception handling
|
||||
// logic to only execute |finally| blocks and then return to the caller
|
||||
// without throwing. Furthermore, because it only executes |finally|
|
||||
// blocks, it's unlikely to benefit from JIT compilation.
|
||||
|
||||
ValueOperand retVal = regs.takeAnyValue();
|
||||
masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), retVal);
|
||||
|
||||
prepareVMCall();
|
||||
pushArg(ImmGCPtr(cx->names().close));
|
||||
pushArg(retVal);
|
||||
pushArg(genObj);
|
||||
|
||||
if (!callVM(InterpretResumeInfo))
|
||||
return false;
|
||||
|
||||
frame.popn(2);
|
||||
frame.push(R0);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load callee.
|
||||
Register callee = regs.takeAny();
|
||||
masm.unboxObject(Address(genObj, GeneratorObject::offsetOfCalleeSlot()), callee);
|
||||
|
||||
// Load the script. Note that we don't relazify generator scripts, so it's
|
||||
// guaranteed to be non-lazy.
|
||||
Register scratch1 = regs.takeAny();
|
||||
masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), scratch1);
|
||||
|
||||
// Load the BaselineScript or call a stub if we don't have one.
|
||||
Label interpret;
|
||||
masm.loadPtr(Address(scratch1, JSScript::offsetOfBaselineScript()), scratch1);
|
||||
masm.branchPtr(Assembler::BelowOrEqual, scratch1, ImmPtr(BASELINE_DISABLED_SCRIPT), &interpret);
|
||||
|
||||
// Determine the resume address based on the yieldIndex and the
|
||||
// yieldIndex -> native table in the BaselineScript.
|
||||
Register scratch2 = regs.takeAny();
|
||||
masm.load32(Address(scratch1, BaselineScript::offsetOfYieldEntriesOffset()), scratch2);
|
||||
masm.addPtr(scratch2, scratch1);
|
||||
masm.unboxInt32(Address(genObj, GeneratorObject::offsetOfYieldIndexSlot()), scratch2);
|
||||
masm.loadPtr(BaseIndex(scratch1, scratch2, ScaleFromElemWidth(sizeof(uintptr_t))), scratch1);
|
||||
|
||||
// Push |undefined| for all formals.
|
||||
Label loop, loopDone;
|
||||
masm.load16ZeroExtend(Address(callee, JSFunction::offsetOfNargs()), scratch2);
|
||||
masm.bind(&loop);
|
||||
masm.branchTest32(Assembler::Zero, scratch2, scratch2, &loopDone);
|
||||
{
|
||||
masm.pushValue(UndefinedValue());
|
||||
masm.sub32(Imm32(1), scratch2);
|
||||
masm.jump(&loop);
|
||||
}
|
||||
masm.bind(&loopDone);
|
||||
|
||||
// Push |this|.
|
||||
masm.pushValue(Address(genObj, GeneratorObject::offsetOfThisSlot()));
|
||||
|
||||
// Update BaselineFrame frameSize field and create the frame descriptor.
|
||||
masm.computeEffectiveAddress(Address(BaselineFrameReg, BaselineFrame::FramePointerOffset),
|
||||
scratch2);
|
||||
masm.subPtr(BaselineStackReg, scratch2);
|
||||
masm.store32(scratch2, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
|
||||
masm.makeFrameDescriptor(scratch2, JitFrame_BaselineJS);
|
||||
|
||||
masm.Push(Imm32(0)); // actual argc
|
||||
masm.PushCalleeToken(callee, false);
|
||||
masm.Push(scratch2); // frame descriptor
|
||||
|
||||
regs.add(callee);
|
||||
|
||||
// Load the return value.
|
||||
ValueOperand retVal = regs.takeAnyValue();
|
||||
masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), retVal);
|
||||
|
||||
// Push a fake return address on the stack. We will resume here when the
|
||||
// generator returns.
|
||||
Label genStart, returnTarget;
|
||||
masm.callAndPushReturnAddress(&genStart);
|
||||
|
||||
// Add an IC entry so the return offset -> pc mapping works.
|
||||
ICEntry icEntry(script->pcToOffset(pc), ICEntry::Kind_Op);
|
||||
icEntry.setReturnOffset(CodeOffsetLabel(masm.currentOffset()));
|
||||
if (!icEntries_.append(icEntry))
|
||||
return false;
|
||||
|
||||
masm.jump(&returnTarget);
|
||||
masm.bind(&genStart);
|
||||
|
||||
// Construct BaselineFrame.
|
||||
masm.push(BaselineFrameReg);
|
||||
masm.mov(BaselineStackReg, BaselineFrameReg);
|
||||
masm.subPtr(Imm32(BaselineFrame::Size()), BaselineStackReg);
|
||||
masm.checkStackAlignment();
|
||||
|
||||
// Store flags and scope chain.
|
||||
masm.store32(Imm32(BaselineFrame::HAS_CALL_OBJ), frame.addressOfFlags());
|
||||
masm.unboxObject(Address(genObj, GeneratorObject::offsetOfScopeChainSlot()), scratch2);
|
||||
masm.storePtr(scratch2, frame.addressOfScopeChain());
|
||||
|
||||
// Store the arguments object if there is one.
|
||||
Label noArgsObj;
|
||||
masm.unboxObject(Address(genObj, GeneratorObject::offsetOfArgsObjSlot()), scratch2);
|
||||
masm.branchTestPtr(Assembler::Zero, scratch2, scratch2, &noArgsObj);
|
||||
{
|
||||
masm.storePtr(scratch2, frame.addressOfArgsObj());
|
||||
masm.or32(Imm32(BaselineFrame::HAS_ARGS_OBJ), frame.addressOfFlags());
|
||||
}
|
||||
masm.bind(&noArgsObj);
|
||||
|
||||
// Push expression slots if needed.
|
||||
Label noExprStack;
|
||||
Address exprStackSlot(genObj, GeneratorObject::offsetOfExpressionStackSlot());
|
||||
masm.branchTestNull(Assembler::Equal, exprStackSlot, &noExprStack);
|
||||
{
|
||||
masm.unboxObject(exprStackSlot, scratch2);
|
||||
|
||||
Register initLength = regs.takeAny();
|
||||
masm.loadPtr(Address(scratch2, NativeObject::offsetOfElements()), scratch2);
|
||||
masm.load32(Address(scratch2, ObjectElements::offsetOfInitializedLength()), initLength);
|
||||
|
||||
Label loop, loopDone;
|
||||
masm.bind(&loop);
|
||||
masm.branchTest32(Assembler::Zero, initLength, initLength, &loopDone);
|
||||
{
|
||||
masm.pushValue(Address(scratch2, 0));
|
||||
masm.addPtr(Imm32(sizeof(Value)), scratch2);
|
||||
masm.sub32(Imm32(1), initLength);
|
||||
masm.jump(&loop);
|
||||
}
|
||||
masm.bind(&loopDone);
|
||||
|
||||
masm.storeValue(NullValue(), exprStackSlot);
|
||||
regs.add(initLength);
|
||||
}
|
||||
|
||||
masm.bind(&noExprStack);
|
||||
masm.pushValue(retVal);
|
||||
|
||||
if (resumeKind == GeneratorObject::NEXT) {
|
||||
// Mark as running and jump to the generator's JIT code.
|
||||
masm.storeValue(Int32Value(0), Address(genObj, GeneratorObject::offsetOfBytecodeOffsetSlot()));
|
||||
masm.jump(scratch1);
|
||||
} else {
|
||||
MOZ_ASSERT(resumeKind == GeneratorObject::THROW);
|
||||
|
||||
// Update the frame's frameSize field.
|
||||
Register scratch3 = regs.takeAny();
|
||||
masm.computeEffectiveAddress(Address(BaselineFrameReg, BaselineFrame::FramePointerOffset),
|
||||
scratch2);
|
||||
masm.movePtr(scratch2, scratch3);
|
||||
masm.subPtr(BaselineStackReg, scratch2);
|
||||
masm.store32(scratch2, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
|
||||
|
||||
prepareVMCall();
|
||||
pushArg(retVal);
|
||||
pushArg(genObj);
|
||||
|
||||
JitCode *code = cx->runtime()->jitRuntime()->getVMWrapper(GeneratorThrowInfo);
|
||||
if (!code)
|
||||
return false;
|
||||
|
||||
// Create the frame descriptor.
|
||||
masm.subPtr(BaselineStackReg, scratch3);
|
||||
masm.makeFrameDescriptor(scratch3, JitFrame_BaselineJS);
|
||||
|
||||
// Push the frame descriptor and the native address of the op after the
|
||||
// YIELD. The frame iteration code relies on this address to determine
|
||||
// where we threw the exception.
|
||||
masm.push(scratch3);
|
||||
masm.push(scratch1);
|
||||
masm.jump(code);
|
||||
regs.add(scratch3);
|
||||
}
|
||||
|
||||
// If the generator script has no JIT code, call into the VM.
|
||||
masm.bind(&interpret);
|
||||
|
||||
prepareVMCall();
|
||||
if (resumeKind == GeneratorObject::NEXT) {
|
||||
pushArg(ImmGCPtr(cx->names().next));
|
||||
} else {
|
||||
MOZ_ASSERT(resumeKind == GeneratorObject::THROW);
|
||||
pushArg(ImmGCPtr(cx->names().throw_));
|
||||
}
|
||||
|
||||
masm.loadValue(frame.addressOfStackValue(frame.peek(-1)), retVal);
|
||||
pushArg(retVal);
|
||||
pushArg(genObj);
|
||||
|
||||
if (!callVM(InterpretResumeInfo))
|
||||
return false;
|
||||
|
||||
// After the generator returns, we restore the stack pointer, push the
|
||||
// return value and we're done.
|
||||
masm.bind(&returnTarget);
|
||||
masm.computeEffectiveAddress(frame.addressOfStackValue(frame.peek(-1)), BaselineStackReg);
|
||||
frame.popn(2);
|
||||
frame.push(R0);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -177,6 +177,7 @@ namespace jit {
|
|||
_(JSOP_INITIALYIELD) \
|
||||
_(JSOP_YIELD) \
|
||||
_(JSOP_FINALYIELDRVAL) \
|
||||
_(JSOP_RESUME) \
|
||||
_(JSOP_CALLEE) \
|
||||
_(JSOP_SETRVAL) \
|
||||
_(JSOP_RETRVAL) \
|
||||
|
|
|
@ -290,6 +290,9 @@ class FrameInfo
|
|||
Address addressOfReturnValue() const {
|
||||
return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfReturnValue());
|
||||
}
|
||||
Address addressOfArgsObj() const {
|
||||
return Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfArgsObj());
|
||||
}
|
||||
Address addressOfStackValue(const StackValue *value) const {
|
||||
MOZ_ASSERT(value->kind() == StackValue::Stack);
|
||||
size_t slot = value - &stack[0];
|
||||
|
|
|
@ -880,6 +880,39 @@ FinalSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbytecode *
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
InterpretResume(JSContext *cx, HandleObject obj, HandleValue val, HandlePropertyName kind,
|
||||
MutableHandleValue rval)
|
||||
{
|
||||
MOZ_ASSERT(obj->is<GeneratorObject>());
|
||||
|
||||
RootedValue selfHostedFun(cx);
|
||||
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().InterpretGeneratorResume,
|
||||
&selfHostedFun))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(selfHostedFun.toObject().is<JSFunction>());
|
||||
|
||||
InvokeArgs args(cx);
|
||||
if (!args.init(3))
|
||||
return false;
|
||||
|
||||
args.setCallee(selfHostedFun);
|
||||
args.setThis(UndefinedValue());
|
||||
|
||||
args[0].setObject(*obj);
|
||||
args[1].set(val);
|
||||
args[2].setString(kind);
|
||||
|
||||
if (!Invoke(cx, args))
|
||||
return false;
|
||||
|
||||
rval.set(args.rval());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
StrictEvalPrologue(JSContext *cx, BaselineFrame *frame)
|
||||
{
|
||||
|
|
|
@ -711,6 +711,8 @@ bool InitialSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbyt
|
|||
bool NormalSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbytecode *pc,
|
||||
uint32_t stackDepth);
|
||||
bool FinalSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbytecode *pc);
|
||||
bool InterpretResume(JSContext *cx, HandleObject obj, HandleValue val, HandlePropertyName kind,
|
||||
MutableHandleValue rval);
|
||||
|
||||
bool StrictEvalPrologue(JSContext *cx, BaselineFrame *frame);
|
||||
bool HeavyweightFunPrologue(JSContext *cx, BaselineFrame *frame);
|
||||
|
|
|
@ -589,6 +589,10 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
|||
call(label);
|
||||
append(desc, currentOffset(), framePushed_);
|
||||
}
|
||||
void callAndPushReturnAddress(Label *label) {
|
||||
ma_push(pc);
|
||||
call(label);
|
||||
}
|
||||
|
||||
void branch(JitCode *c) {
|
||||
BufferOffset bo = m_buffer.nextOffset();
|
||||
|
@ -690,6 +694,9 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
|||
void jump(Label *label) {
|
||||
as_b(label);
|
||||
}
|
||||
void jump(JitCode *code) {
|
||||
branch(code);
|
||||
}
|
||||
void jump(Register reg) {
|
||||
ma_bx(reg);
|
||||
}
|
||||
|
|
|
@ -579,6 +579,9 @@ class MacroAssemblerX86Shared : public Assembler
|
|||
void jump(Label *label) {
|
||||
jmp(label);
|
||||
}
|
||||
void jump(JitCode *code) {
|
||||
jmp(code);
|
||||
}
|
||||
void jump(RepatchLabel *label) {
|
||||
jmp(label);
|
||||
}
|
||||
|
@ -1197,6 +1200,9 @@ class MacroAssemblerX86Shared : public Assembler
|
|||
mov(target, eax);
|
||||
call(eax);
|
||||
}
|
||||
void callAndPushReturnAddress(Label *label) {
|
||||
call(label);
|
||||
}
|
||||
|
||||
void checkStackAlignment() {
|
||||
// Exists for ARM compatibility.
|
||||
|
|
|
@ -943,6 +943,10 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
|
|||
cmpl(ToUpper32(operand), Imm32(Upper32Of(GetShiftedTag(JSVAL_TYPE_NULL))));
|
||||
j(cond, label);
|
||||
}
|
||||
void branchTestNull(Condition cond, const Address &address, Label *label) {
|
||||
MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
||||
branchTestNull(cond, Operand(address), label);
|
||||
}
|
||||
|
||||
// Perform a type-test on a full Value loaded into a register.
|
||||
// Clobbers the ScratchReg.
|
||||
|
|
|
@ -377,7 +377,14 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
|
|||
Condition testUndefined(Condition cond, const Address &addr) {
|
||||
return testUndefined(cond, Operand(addr));
|
||||
}
|
||||
|
||||
Condition testNull(Condition cond, const Operand &operand) {
|
||||
MOZ_ASSERT(cond == Equal || cond == NotEqual);
|
||||
cmpl(ToType(operand), ImmTag(JSVAL_TAG_NULL));
|
||||
return cond;
|
||||
}
|
||||
Condition testNull(Condition cond, const Address &addr) {
|
||||
return testNull(cond, Operand(addr));
|
||||
}
|
||||
|
||||
Condition testUndefined(Condition cond, const ValueOperand &value) {
|
||||
return testUndefined(cond, value.typeReg());
|
||||
|
|
|
@ -110,6 +110,18 @@ GeneratorObject::finalSuspend(JSContext *cx, HandleObject obj)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
js::GeneratorThrow(JSContext *cx, HandleObject obj, HandleValue arg)
|
||||
{
|
||||
GeneratorObject *genObj = &obj->as<GeneratorObject>();
|
||||
cx->setPendingException(arg);
|
||||
if (genObj->isNewborn())
|
||||
genObj->setClosed();
|
||||
else
|
||||
genObj->setRunning();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
GeneratorObject::resume(JSContext *cx, InterpreterActivation &activation,
|
||||
HandleObject obj, HandleValue arg, GeneratorObject::ResumeKind resumeKind)
|
||||
|
@ -159,12 +171,7 @@ GeneratorObject::resume(JSContext *cx, InterpreterActivation &activation,
|
|||
return true;
|
||||
|
||||
case THROW:
|
||||
cx->setPendingException(arg);
|
||||
if (genObj->isNewborn())
|
||||
genObj->setClosed();
|
||||
else
|
||||
genObj->setRunning();
|
||||
return false;
|
||||
return GeneratorThrow(cx, genObj, arg);
|
||||
|
||||
case CLOSE:
|
||||
MOZ_ASSERT(genObj->is<LegacyGeneratorObject>());
|
||||
|
|
|
@ -230,6 +230,8 @@ class StarGeneratorObject : public GeneratorObject
|
|||
static const Class class_;
|
||||
};
|
||||
|
||||
bool GeneratorThrow(JSContext *cx, HandleObject obj, HandleValue val);
|
||||
|
||||
} // namespace js
|
||||
|
||||
template<>
|
||||
|
|
Загрузка…
Ссылка в новой задаче