mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-27 07:34:20 +00:00
Bug 866878 - Support try-finally in the baseline compiler. r=djvj
--HG-- extra : rebase_source : c5bd88422ce39de94888fad060259ef2590acb5a
This commit is contained in:
parent
50c72a104f
commit
fa3bea4976
@ -429,6 +429,8 @@ static const VMFunction InterruptCheckInfo = FunctionInfo<InterruptCheckFn>(Inte
|
||||
bool
|
||||
BaselineCompiler::emitInterruptCheck()
|
||||
{
|
||||
frame.syncStack(0);
|
||||
|
||||
Label done;
|
||||
void *interrupt = (void *)&cx->compartment()->rt->interrupt;
|
||||
masm.branch32(Assembler::Equal, AbsoluteAddress(interrupt), Imm32(0), &done);
|
||||
@ -2316,6 +2318,45 @@ BaselineCompiler::emit_JSOP_TRY()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_FINALLY()
|
||||
{
|
||||
// JSOP_FINALLY has a def count of 2, but these values are already on the
|
||||
// stack (they're pushed by JSOP_GOSUB). Update the compiler's stack state.
|
||||
frame.setStackDepth(frame.stackDepth() + 2);
|
||||
|
||||
// To match the interpreter, emit an interrupt check at the start of the
|
||||
// finally block.
|
||||
return emitInterruptCheck();
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_GOSUB()
|
||||
{
|
||||
// Push |false| so that RETSUB knows the value on top of the
|
||||
// stack is not an exception but the offset to the op following
|
||||
// this GOSUB.
|
||||
frame.push(BooleanValue(false));
|
||||
|
||||
int32_t nextOffset = GetNextPc(pc) - script->code;
|
||||
frame.push(Int32Value(nextOffset));
|
||||
|
||||
// Jump to the finally block.
|
||||
frame.syncStack(0);
|
||||
jsbytecode *target = pc + GET_JUMP_OFFSET(pc);
|
||||
masm.jump(labelOf(target));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_RETSUB()
|
||||
{
|
||||
frame.popRegsAndSync(2);
|
||||
|
||||
ICRetSub_Fallback::Compiler stubCompiler(cx);
|
||||
return emitOpIC(stubCompiler.getStub(&stubSpace_));
|
||||
}
|
||||
|
||||
typedef bool (*EnterBlockFn)(JSContext *, BaselineFrame *, Handle<StaticBlockObject *>);
|
||||
static const VMFunction EnterBlockInfo = FunctionInfo<EnterBlockFn>(ion::EnterBlock);
|
||||
|
||||
|
@ -154,6 +154,9 @@ namespace ion {
|
||||
_(JSOP_SETCALL) \
|
||||
_(JSOP_THROW) \
|
||||
_(JSOP_TRY) \
|
||||
_(JSOP_FINALLY) \
|
||||
_(JSOP_GOSUB) \
|
||||
_(JSOP_RETSUB) \
|
||||
_(JSOP_ENTERBLOCK) \
|
||||
_(JSOP_ENTERLET0) \
|
||||
_(JSOP_ENTERLET1) \
|
||||
|
@ -132,6 +132,9 @@ BaselineFrame::initForOsr(StackFrame *fp, uint32_t numStackValues)
|
||||
hookData_ = fp->hookData();
|
||||
}
|
||||
|
||||
if (fp->hasReturnValue())
|
||||
setReturnValue(fp->returnValue());
|
||||
|
||||
if (fp->hasPushedSPSFrame())
|
||||
flags_ |= BaselineFrame::HAS_PUSHED_SPS_FRAME;
|
||||
|
||||
|
@ -8197,6 +8197,111 @@ ICRest_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
return tailCallVM(DoCreateRestParameterInfo, masm);
|
||||
}
|
||||
|
||||
static bool
|
||||
DoRetSubFallback(JSContext *cx, BaselineFrame *frame, ICRetSub_Fallback *stub,
|
||||
HandleValue val, uint8_t **resumeAddr)
|
||||
{
|
||||
FallbackICSpew(cx, stub, "RetSub");
|
||||
|
||||
// |val| is the bytecode offset where we should resume.
|
||||
|
||||
JS_ASSERT(val.isInt32());
|
||||
JS_ASSERT(val.toInt32() >= 0);
|
||||
|
||||
JSScript *script = frame->script();
|
||||
uint32_t offset = uint32_t(val.toInt32());
|
||||
JS_ASSERT(offset < script->length);
|
||||
|
||||
*resumeAddr = script->baselineScript()->nativeCodeForPC(script, script->code + offset);
|
||||
|
||||
if (stub->numOptimizedStubs() >= ICRetSub_Fallback::MAX_OPTIMIZED_STUBS)
|
||||
return true;
|
||||
|
||||
// Attach an optimized stub for this pc offset.
|
||||
IonSpew(IonSpew_BaselineIC, " Generating RetSub stub for pc offset %u", offset);
|
||||
ICRetSub_Resume::Compiler compiler(cx, offset, *resumeAddr);
|
||||
ICStub *optStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!optStub)
|
||||
return false;
|
||||
|
||||
stub->addNewStub(optStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef bool(*DoRetSubFallbackFn)(JSContext *cx, BaselineFrame *, ICRetSub_Fallback *,
|
||||
HandleValue, uint8_t **);
|
||||
static const VMFunction DoRetSubFallbackInfo = FunctionInfo<DoRetSubFallbackFn>(DoRetSubFallback);
|
||||
|
||||
typedef bool (*ThrowFn)(JSContext *, HandleValue);
|
||||
static const VMFunction ThrowInfo = FunctionInfo<ThrowFn>(js::Throw);
|
||||
|
||||
bool
|
||||
ICRetSub_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
{
|
||||
// If R0 is BooleanValue(true), rethrow R1.
|
||||
Label rethrow;
|
||||
masm.branchTestBooleanTruthy(true, R0, &rethrow);
|
||||
{
|
||||
// Call a stub to get the native code address for the pc offset in R1.
|
||||
GeneralRegisterSet regs(availableGeneralRegs(0));
|
||||
regs.take(R1);
|
||||
regs.takeUnchecked(BaselineTailCallReg);
|
||||
|
||||
Register frame = regs.takeAny();
|
||||
masm.movePtr(BaselineFrameReg, frame);
|
||||
|
||||
enterStubFrame(masm, regs.getAny());
|
||||
|
||||
masm.pushValue(R1);
|
||||
masm.push(BaselineStubReg);
|
||||
masm.pushBaselineFramePtr(frame, frame);
|
||||
|
||||
if (!callVM(DoRetSubFallbackInfo, masm))
|
||||
return false;
|
||||
|
||||
leaveStubFrame(masm);
|
||||
|
||||
EmitChangeICReturnAddress(masm, ReturnReg);
|
||||
EmitReturnFromIC(masm);
|
||||
}
|
||||
|
||||
masm.bind(&rethrow);
|
||||
EmitRestoreTailCallReg(masm);
|
||||
masm.pushValue(R1);
|
||||
return tailCallVM(ThrowInfo, masm);
|
||||
}
|
||||
|
||||
bool
|
||||
ICRetSub_Resume::Compiler::generateStubCode(MacroAssembler &masm)
|
||||
{
|
||||
// If R0 is BooleanValue(true), rethrow R1.
|
||||
Label fail, rethrow;
|
||||
masm.branchTestBooleanTruthy(true, R0, &rethrow);
|
||||
|
||||
// R1 is the pc offset. Ensure it matches this stub's offset.
|
||||
Register offset = masm.extractInt32(R1, ExtractTemp0);
|
||||
masm.branch32(Assembler::NotEqual,
|
||||
Address(BaselineStubReg, ICRetSub_Resume::offsetOfPCOffset()),
|
||||
offset,
|
||||
&fail);
|
||||
|
||||
// pc offset matches, resume at the target pc.
|
||||
masm.loadPtr(Address(BaselineStubReg, ICRetSub_Resume::offsetOfAddr()), R0.scratchReg());
|
||||
EmitChangeICReturnAddress(masm, R0.scratchReg());
|
||||
EmitReturnFromIC(masm);
|
||||
|
||||
// Rethrow the Value stored in R1.
|
||||
masm.bind(&rethrow);
|
||||
EmitRestoreTailCallReg(masm);
|
||||
masm.pushValue(R1);
|
||||
if (!tailCallVM(ThrowInfo, masm))
|
||||
return false;
|
||||
|
||||
masm.bind(&fail);
|
||||
EmitStubGuardFailure(masm);
|
||||
return true;
|
||||
}
|
||||
|
||||
ICProfiler_PushFunction::ICProfiler_PushFunction(IonCode *stubCode, const char *str,
|
||||
HandleScript script)
|
||||
: ICStub(ICStub::Profiler_PushFunction, stubCode),
|
||||
|
@ -392,7 +392,10 @@ class ICEntry
|
||||
_(TypeOf_Fallback) \
|
||||
_(TypeOf_Typed) \
|
||||
\
|
||||
_(Rest_Fallback)
|
||||
_(Rest_Fallback) \
|
||||
\
|
||||
_(RetSub_Fallback) \
|
||||
_(RetSub_Resume)
|
||||
|
||||
#define FORWARD_DECLARE_STUBS(kindName) class IC##kindName;
|
||||
IC_STUB_KIND_LIST(FORWARD_DECLARE_STUBS)
|
||||
@ -740,6 +743,7 @@ class ICStub
|
||||
case GetProp_DOMProxyShadowed:
|
||||
case SetProp_CallScripted:
|
||||
case SetProp_CallNative:
|
||||
case RetSub_Fallback:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@ -5547,6 +5551,89 @@ class ICRest_Fallback : public ICFallbackStub
|
||||
};
|
||||
};
|
||||
|
||||
// Stub for JSOP_RETSUB ("returning" from a |finally| block).
|
||||
class ICRetSub_Fallback : public ICFallbackStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
ICRetSub_Fallback(IonCode *stubCode)
|
||||
: ICFallbackStub(ICStub::RetSub_Fallback, stubCode)
|
||||
{ }
|
||||
|
||||
public:
|
||||
static const uint32_t MAX_OPTIMIZED_STUBS = 8;
|
||||
|
||||
static inline ICRetSub_Fallback *New(ICStubSpace *space, IonCode *code) {
|
||||
if (!code)
|
||||
return NULL;
|
||||
return space->allocate<ICRetSub_Fallback>(code);
|
||||
}
|
||||
|
||||
class Compiler : public ICStubCompiler {
|
||||
protected:
|
||||
bool generateStubCode(MacroAssembler &masm);
|
||||
|
||||
public:
|
||||
Compiler(JSContext *cx)
|
||||
: ICStubCompiler(cx, ICStub::RetSub_Fallback)
|
||||
{ }
|
||||
|
||||
ICStub *getStub(ICStubSpace *space) {
|
||||
return ICRetSub_Fallback::New(space, getStubCode());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Optimized JSOP_RETSUB stub. Every stub maps a single pc offset to its
|
||||
// native code address.
|
||||
class ICRetSub_Resume : public ICStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
protected:
|
||||
uint32_t pcOffset_;
|
||||
uint8_t *addr_;
|
||||
|
||||
ICRetSub_Resume(IonCode *stubCode, uint32_t pcOffset, uint8_t *addr)
|
||||
: ICStub(ICStub::RetSub_Resume, stubCode),
|
||||
pcOffset_(pcOffset),
|
||||
addr_(addr)
|
||||
{ }
|
||||
|
||||
public:
|
||||
static ICRetSub_Resume *New(ICStubSpace *space, IonCode *code, uint32_t pcOffset,
|
||||
uint8_t *addr) {
|
||||
if (!code)
|
||||
return NULL;
|
||||
return space->allocate<ICRetSub_Resume>(code, pcOffset, addr);
|
||||
}
|
||||
|
||||
static size_t offsetOfPCOffset() {
|
||||
return offsetof(ICRetSub_Resume, pcOffset_);
|
||||
}
|
||||
static size_t offsetOfAddr() {
|
||||
return offsetof(ICRetSub_Resume, addr_);
|
||||
}
|
||||
|
||||
class Compiler : public ICStubCompiler {
|
||||
uint32_t pcOffset_;
|
||||
uint8_t *addr_;
|
||||
|
||||
bool generateStubCode(MacroAssembler &masm);
|
||||
|
||||
public:
|
||||
Compiler(JSContext *cx, uint32_t pcOffset, uint8_t *addr)
|
||||
: ICStubCompiler(cx, ICStub::RetSub_Resume),
|
||||
pcOffset_(pcOffset),
|
||||
addr_(addr)
|
||||
{ }
|
||||
|
||||
ICStub *getStub(ICStubSpace *space) {
|
||||
return ICRetSub_Resume::New(space, getStubCode(), pcOffset_, addr_);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace ion
|
||||
} // namespace js
|
||||
|
||||
|
@ -429,6 +429,17 @@ HandleException(JSContext *cx, const IonFrameIterator &frame, ResumeFromExceptio
|
||||
}
|
||||
break;
|
||||
|
||||
case JSTRY_FINALLY:
|
||||
if (cx->isExceptionPending()) {
|
||||
rfe->kind = ResumeFromException::RESUME_FINALLY;
|
||||
jsbytecode *finallyPC = script->main() + tn->start + tn->length;
|
||||
rfe->target = script->baselineScript()->nativeCodeForPC(script, finallyPC);
|
||||
rfe->exception = cx->getPendingException();
|
||||
cx->clearPendingException();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case JSTRY_ITER: {
|
||||
Value iterValue(* (Value *) rfe->stackPointer);
|
||||
RootedObject iterObject(cx, &iterValue.toObject());
|
||||
|
@ -259,12 +259,16 @@ struct ResumeFromException
|
||||
{
|
||||
static const uint32_t RESUME_ENTRY_FRAME = 0;
|
||||
static const uint32_t RESUME_CATCH = 1;
|
||||
static const uint32_t RESUME_FORCED_RETURN = 2;
|
||||
static const uint32_t RESUME_FINALLY = 2;
|
||||
static const uint32_t RESUME_FORCED_RETURN = 3;
|
||||
|
||||
uint8_t *framePointer;
|
||||
uint8_t *stackPointer;
|
||||
uint8_t *target;
|
||||
uint32_t kind;
|
||||
|
||||
// Value to push when resuming into a |finally| block.
|
||||
Value exception;
|
||||
};
|
||||
|
||||
void HandleException(ResumeFromException *rfe);
|
||||
|
@ -20,6 +20,7 @@ enum DataType {
|
||||
Type_Void,
|
||||
Type_Bool,
|
||||
Type_Int32,
|
||||
Type_Pointer,
|
||||
Type_Object,
|
||||
Type_Value,
|
||||
Type_Handle,
|
||||
@ -328,6 +329,7 @@ template <class> struct OutParamToDataType { static const DataType result = Type
|
||||
template <> struct OutParamToDataType<Value *> { static const DataType result = Type_Value; };
|
||||
template <> struct OutParamToDataType<int *> { static const DataType result = Type_Int32; };
|
||||
template <> struct OutParamToDataType<uint32_t *> { static const DataType result = Type_Int32; };
|
||||
template <> struct OutParamToDataType<uint8_t **> { static const DataType result = Type_Pointer; };
|
||||
template <> struct OutParamToDataType<MutableHandleValue> { static const DataType result = Type_Handle; };
|
||||
template <> struct OutParamToDataType<MutableHandleObject> { static const DataType result = Type_Handle; };
|
||||
|
||||
|
@ -3219,13 +3219,15 @@ MacroAssemblerARMCompat::handleFailureWithHandler(void *handler)
|
||||
passABIArg(r0);
|
||||
callWithABI(handler);
|
||||
|
||||
Label catch_;
|
||||
Label entryFrame;
|
||||
Label catch_;
|
||||
Label finally;
|
||||
Label return_;
|
||||
|
||||
ma_ldr(Operand(sp, offsetof(ResumeFromException, kind)), r0);
|
||||
branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame);
|
||||
branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_CATCH), &catch_);
|
||||
branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_FINALLY), &finally);
|
||||
branch32(Assembler::Equal, r0, Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_);
|
||||
|
||||
breakpoint(); // Invalid kind.
|
||||
@ -3248,6 +3250,21 @@ MacroAssemblerARMCompat::handleFailureWithHandler(void *handler)
|
||||
ma_ldr(Operand(sp, offsetof(ResumeFromException, stackPointer)), sp);
|
||||
jump(r0);
|
||||
|
||||
// If we found a finally block, this must be a baseline frame. Push
|
||||
// two values expected by JSOP_RETSUB: BooleanValue(true) and the
|
||||
// exception.
|
||||
bind(&finally);
|
||||
ValueOperand exception = ValueOperand(r1, r2);
|
||||
loadValue(Operand(sp, offsetof(ResumeFromException, exception)), exception);
|
||||
|
||||
ma_ldr(Operand(sp, offsetof(ResumeFromException, target)), r0);
|
||||
ma_ldr(Operand(sp, offsetof(ResumeFromException, framePointer)), r11);
|
||||
ma_ldr(Operand(sp, offsetof(ResumeFromException, stackPointer)), sp);
|
||||
|
||||
pushValue(BooleanValue(true));
|
||||
pushValue(exception);
|
||||
jump(r0);
|
||||
|
||||
// Only used in debug mode. Return BaselineFrame->returnValue() to the caller.
|
||||
bind(&return_);
|
||||
ma_ldr(Operand(sp, offsetof(ResumeFromException, framePointer)), r11);
|
||||
|
@ -634,6 +634,7 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
|
||||
break;
|
||||
|
||||
case Type_Int32:
|
||||
case Type_Pointer:
|
||||
outReg = r4;
|
||||
regs.take(outReg);
|
||||
masm.reserveStack(sizeof(int32_t));
|
||||
@ -710,6 +711,7 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
|
||||
break;
|
||||
|
||||
case Type_Int32:
|
||||
case Type_Pointer:
|
||||
masm.load32(Address(sp, 0), ReturnReg);
|
||||
masm.freeStack(sizeof(int32_t));
|
||||
break;
|
||||
|
@ -189,16 +189,16 @@ MacroAssemblerX64::handleFailureWithHandler(void *handler)
|
||||
passABIArg(rax);
|
||||
callWithABI(handler);
|
||||
|
||||
Label catch_;
|
||||
Label entryFrame;
|
||||
Label catch_;
|
||||
Label finally;
|
||||
Label return_;
|
||||
|
||||
branch32(Assembler::Equal, Address(rsp, offsetof(ResumeFromException, kind)),
|
||||
Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame);
|
||||
branch32(Assembler::Equal, Address(rsp, offsetof(ResumeFromException, kind)),
|
||||
Imm32(ResumeFromException::RESUME_CATCH), &catch_);
|
||||
branch32(Assembler::Equal, Address(esp, offsetof(ResumeFromException, kind)),
|
||||
Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_);
|
||||
loadPtr(Address(rsp, offsetof(ResumeFromException, kind)), rax);
|
||||
branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame);
|
||||
branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_CATCH), &catch_);
|
||||
branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_FINALLY), &finally);
|
||||
branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_);
|
||||
|
||||
breakpoint(); // Invalid kind.
|
||||
|
||||
@ -217,6 +217,21 @@ MacroAssemblerX64::handleFailureWithHandler(void *handler)
|
||||
movq(Operand(rsp, offsetof(ResumeFromException, stackPointer)), rsp);
|
||||
jmp(Operand(rax));
|
||||
|
||||
// If we found a finally block, this must be a baseline frame. Push
|
||||
// two values expected by JSOP_RETSUB: BooleanValue(true) and the
|
||||
// exception.
|
||||
bind(&finally);
|
||||
ValueOperand exception = ValueOperand(rcx);
|
||||
loadValue(Operand(esp, offsetof(ResumeFromException, exception)), exception);
|
||||
|
||||
movq(Operand(rsp, offsetof(ResumeFromException, target)), rax);
|
||||
movq(Operand(rsp, offsetof(ResumeFromException, framePointer)), rbp);
|
||||
movq(Operand(rsp, offsetof(ResumeFromException, stackPointer)), rsp);
|
||||
|
||||
pushValue(BooleanValue(true));
|
||||
pushValue(exception);
|
||||
jmp(Operand(rax));
|
||||
|
||||
// Only used in debug mode. Return BaselineFrame->returnValue() to the caller.
|
||||
bind(&return_);
|
||||
movq(Operand(rsp, offsetof(ResumeFromException, framePointer)), rbp);
|
||||
|
@ -538,6 +538,12 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
|
||||
masm.movq(esp, outReg);
|
||||
break;
|
||||
|
||||
case Type_Pointer:
|
||||
outReg = regs.takeAny();
|
||||
masm.reserveStack(sizeof(uintptr_t));
|
||||
masm.movq(esp, outReg);
|
||||
break;
|
||||
|
||||
default:
|
||||
JS_ASSERT(f.outParam == Type_Void);
|
||||
break;
|
||||
@ -612,6 +618,11 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
|
||||
masm.freeStack(sizeof(int32_t));
|
||||
break;
|
||||
|
||||
case Type_Pointer:
|
||||
masm.loadPtr(Address(esp, 0), ReturnReg);
|
||||
masm.freeStack(sizeof(uintptr_t));
|
||||
break;
|
||||
|
||||
default:
|
||||
JS_ASSERT(f.outParam == Type_Void);
|
||||
break;
|
||||
|
@ -204,16 +204,16 @@ MacroAssemblerX86::handleFailureWithHandler(void *handler)
|
||||
passABIArg(eax);
|
||||
callWithABI(handler);
|
||||
|
||||
Label catch_;
|
||||
Label entryFrame;
|
||||
Label catch_;
|
||||
Label finally;
|
||||
Label return_;
|
||||
|
||||
branch32(Assembler::Equal, Address(esp, offsetof(ResumeFromException, kind)),
|
||||
Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame);
|
||||
branch32(Assembler::Equal, Address(esp, offsetof(ResumeFromException, kind)),
|
||||
Imm32(ResumeFromException::RESUME_CATCH), &catch_);
|
||||
branch32(Assembler::Equal, Address(esp, offsetof(ResumeFromException, kind)),
|
||||
Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_);
|
||||
loadPtr(Address(esp, offsetof(ResumeFromException, kind)), eax);
|
||||
branch32(Assembler::Equal, eax, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame);
|
||||
branch32(Assembler::Equal, eax, Imm32(ResumeFromException::RESUME_CATCH), &catch_);
|
||||
branch32(Assembler::Equal, eax, Imm32(ResumeFromException::RESUME_FINALLY), &finally);
|
||||
branch32(Assembler::Equal, eax, Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_);
|
||||
|
||||
breakpoint(); // Invalid kind.
|
||||
|
||||
@ -232,6 +232,21 @@ MacroAssemblerX86::handleFailureWithHandler(void *handler)
|
||||
movl(Operand(esp, offsetof(ResumeFromException, stackPointer)), esp);
|
||||
jmp(Operand(eax));
|
||||
|
||||
// If we found a finally block, this must be a baseline frame. Push
|
||||
// two values expected by JSOP_RETSUB: BooleanValue(true) and the
|
||||
// exception.
|
||||
bind(&finally);
|
||||
ValueOperand exception = ValueOperand(ecx, edx);
|
||||
loadValue(Operand(esp, offsetof(ResumeFromException, exception)), exception);
|
||||
|
||||
movl(Operand(esp, offsetof(ResumeFromException, target)), eax);
|
||||
movl(Operand(esp, offsetof(ResumeFromException, framePointer)), ebp);
|
||||
movl(Operand(esp, offsetof(ResumeFromException, stackPointer)), esp);
|
||||
|
||||
pushValue(BooleanValue(true));
|
||||
pushValue(exception);
|
||||
jmp(Operand(eax));
|
||||
|
||||
// Only used in debug mode. Return BaselineFrame->returnValue() to the caller.
|
||||
bind(&return_);
|
||||
movl(Operand(esp, offsetof(ResumeFromException, framePointer)), ebp);
|
||||
|
@ -550,6 +550,7 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
|
||||
break;
|
||||
|
||||
case Type_Int32:
|
||||
case Type_Pointer:
|
||||
outReg = regs.takeAny();
|
||||
masm.reserveStack(sizeof(int32_t));
|
||||
masm.movl(esp, outReg);
|
||||
@ -630,8 +631,9 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
|
||||
break;
|
||||
|
||||
case Type_Int32:
|
||||
case Type_Pointer:
|
||||
masm.load32(Address(esp, 0), ReturnReg);
|
||||
masm.freeStack(sizeof(JSBool));
|
||||
masm.freeStack(sizeof(int32_t));
|
||||
break;
|
||||
|
||||
default:
|
||||
|
28
js/src/jit-test/tests/baseline/try-finally-1.js
Normal file
28
js/src/jit-test/tests/baseline/try-finally-1.js
Normal file
@ -0,0 +1,28 @@
|
||||
function test1() {
|
||||
try {
|
||||
return "try";
|
||||
} finally {
|
||||
return "finally";
|
||||
}
|
||||
}
|
||||
assertEq(test1(), "finally");
|
||||
|
||||
function test2() {
|
||||
try {
|
||||
throw 4;
|
||||
} catch(e) {
|
||||
return "catch";
|
||||
} finally {
|
||||
return "finally";
|
||||
}
|
||||
}
|
||||
assertEq(test2(), "finally");
|
||||
|
||||
function test3() {
|
||||
try {
|
||||
throw 4;
|
||||
} finally {
|
||||
return "finally"; // Don't rethrow.
|
||||
}
|
||||
}
|
||||
assertEq(test3(), "finally");
|
37
js/src/jit-test/tests/baseline/try-finally-2.js
Normal file
37
js/src/jit-test/tests/baseline/try-finally-2.js
Normal file
@ -0,0 +1,37 @@
|
||||
var count = 0;
|
||||
function f() {
|
||||
try {
|
||||
try {
|
||||
try {
|
||||
count += 2;
|
||||
} finally {
|
||||
count += 3;
|
||||
throw 3;
|
||||
}
|
||||
} catch(e) {
|
||||
count += 4;
|
||||
throw 4;
|
||||
}
|
||||
} finally {
|
||||
count += 5;
|
||||
try {
|
||||
count += 6;
|
||||
} catch(e) {
|
||||
count += 7;
|
||||
throw 123;
|
||||
} finally {
|
||||
count += 8;
|
||||
}
|
||||
count += 9;
|
||||
}
|
||||
count += 10;
|
||||
}
|
||||
for (var i=0; i<3; i++) {
|
||||
try {
|
||||
f();
|
||||
assertEq(0, 1);
|
||||
} catch(e) {
|
||||
assertEq(e, 4);
|
||||
}
|
||||
}
|
||||
assertEq(count, 111);
|
30
js/src/jit-test/tests/baseline/try-finally-3.js
Normal file
30
js/src/jit-test/tests/baseline/try-finally-3.js
Normal file
@ -0,0 +1,30 @@
|
||||
// Test optimized RetSub stubs.
|
||||
var count = 0;
|
||||
function f(x) {
|
||||
try {
|
||||
if (x < 0)
|
||||
throw "negative";
|
||||
if (x & 1)
|
||||
return "odd";
|
||||
count++;
|
||||
} finally {
|
||||
count += 3;
|
||||
}
|
||||
|
||||
return "even";
|
||||
}
|
||||
for (var i=0; i<15; i++) {
|
||||
var res = f(i);
|
||||
if ((i % 2) === 0)
|
||||
assertEq(res, "even");
|
||||
else
|
||||
assertEq(res, "odd");
|
||||
}
|
||||
try {
|
||||
f(-1);
|
||||
assertEq(0, 1);
|
||||
} catch(e) {
|
||||
assertEq(e, "negative");
|
||||
}
|
||||
|
||||
assertEq(count, 56);
|
29
js/src/jit-test/tests/baseline/try-finally-osr.js
Normal file
29
js/src/jit-test/tests/baseline/try-finally-osr.js
Normal file
@ -0,0 +1,29 @@
|
||||
var count = 0;
|
||||
|
||||
// OSR into a finally block should not throw away the frame's
|
||||
// return value.
|
||||
function test1() {
|
||||
try {
|
||||
return [1, 2, 3];
|
||||
} finally {
|
||||
for (var i=0; i<20; i++) { count++; }
|
||||
}
|
||||
}
|
||||
assertEq(test1().toString(), "1,2,3");
|
||||
assertEq(count, 20);
|
||||
|
||||
// OSR into the finally block, with exception pending.
|
||||
function test2() {
|
||||
try {
|
||||
throw 3;
|
||||
} finally {
|
||||
for (var i=0; i<20; i++) { count++; }
|
||||
}
|
||||
}
|
||||
try {
|
||||
test2();
|
||||
assertEq(0, 1);
|
||||
} catch(e) {
|
||||
assertEq(e, 3);
|
||||
}
|
||||
assertEq(count, 40);
|
@ -22,7 +22,8 @@ if (typeof findReferences == "function") {
|
||||
try {
|
||||
return o;
|
||||
} finally {
|
||||
rvalueCorrect = referencesVia(null, 'rval', o);
|
||||
rvalueCorrect = referencesVia(null, 'rval', o) ||
|
||||
referencesVia(null, 'baseline-rval', o);
|
||||
}
|
||||
}
|
||||
rvalueCorrect = false;
|
||||
|
Loading…
x
Reference in New Issue
Block a user