Bug 1093573 part 10 - Baseline-compile JSOP_RESUME. r=shu,wingo

This commit is contained in:
Jan de Mooij 2014-11-12 12:12:39 +01:00
Родитель 3c38cb2380
Коммит e12021be76
11 изменённых файлов: 306 добавлений и 7 удалений

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

@ -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<>