MIPS: Add a level of indirection to exception handler addresses.
Port r9977 (9aded78be4). Note: This is the port of the reapplied patch, not the original. Original commit message: To support deoptimization of exception handlers, the handler address in the stack is converted to a pair of code object and an index into a separate table of code offsets. The index part is invariant under deoptimization. The index is packed into the handler state field so that handler size does not change. BUG= TEST= Review URL: http://codereview.chromium.org/8557003 Patch from Gergely Kis <gergely@homejinni.com>. git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10039 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
561b5716d7
commit
29b5ceb897
@ -3856,7 +3856,7 @@ void CEntryStub::Generate(MacroAssembler* masm) {
|
||||
|
||||
|
||||
void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
|
||||
Label invoke, exit;
|
||||
Label invoke, handler_entry, exit;
|
||||
Isolate* isolate = masm->isolate();
|
||||
|
||||
// Registers:
|
||||
@ -3933,14 +3933,15 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
|
||||
__ bind(&cont);
|
||||
__ push(t0);
|
||||
|
||||
// Call a faked try-block that does the invoke.
|
||||
__ bal(&invoke); // bal exposes branch delay slot.
|
||||
__ nop(); // Branch delay slot nop.
|
||||
|
||||
// Caught exception: Store result (exception) in the pending
|
||||
// exception field in the JSEnv and return a failure sentinel.
|
||||
// Coming in here the fp will be invalid because the PushTryHandler below
|
||||
// sets it to 0 to signal the existence of the JSEntry frame.
|
||||
// Jump to a faked try block that does the invoke, with a faked catch
|
||||
// block that sets the pending exception.
|
||||
__ jmp(&invoke);
|
||||
__ bind(&handler_entry);
|
||||
handler_offset_ = handler_entry.pos();
|
||||
// Caught exception: Store result (exception) in the pending exception
|
||||
// field in the JSEnv and return a failure sentinel. Coming in here the
|
||||
// fp will be invalid because the PushTryHandler below sets it to 0 to
|
||||
// signal the existence of the JSEntry frame.
|
||||
__ li(t0, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
|
||||
isolate)));
|
||||
__ sw(v0, MemOperand(t0)); // We come back from 'invoke'. result is in v0.
|
||||
@ -3948,9 +3949,10 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
|
||||
__ b(&exit); // b exposes branch delay slot.
|
||||
__ nop(); // Branch delay slot nop.
|
||||
|
||||
// Invoke: Link this frame into the handler chain.
|
||||
// Invoke: Link this frame into the handler chain. There's only one
|
||||
// handler block in this code object, so its index is 0.
|
||||
__ bind(&invoke);
|
||||
__ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER);
|
||||
__ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER, 0);
|
||||
// If an exception not caught by another handler occurs, this handler
|
||||
// returns control to the code after the bal(&invoke) above, which
|
||||
// restores all kCalleeSaved registers (including cp and fp) to their
|
||||
@ -5103,7 +5105,7 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void CallFunctionStub::FinishCode(Code* code) {
|
||||
void CallFunctionStub::FinishCode(Handle<Code> code) {
|
||||
code->set_has_function_cache(false);
|
||||
}
|
||||
|
||||
|
@ -137,7 +137,7 @@ class UnaryOpStub: public CodeStub {
|
||||
return UnaryOpIC::ToState(operand_type_);
|
||||
}
|
||||
|
||||
virtual void FinishCode(Code* code) {
|
||||
virtual void FinishCode(Handle<Code> code) {
|
||||
code->set_unary_op_type(operand_type_);
|
||||
}
|
||||
};
|
||||
@ -236,7 +236,7 @@ class BinaryOpStub: public CodeStub {
|
||||
return BinaryOpIC::ToState(operands_type_);
|
||||
}
|
||||
|
||||
virtual void FinishCode(Code* code) {
|
||||
virtual void FinishCode(Handle<Code> code) {
|
||||
code->set_binary_op_type(operands_type_);
|
||||
code->set_binary_op_result_type(result_type_);
|
||||
}
|
||||
|
@ -154,13 +154,13 @@ static const int kSafepointRegisterStackIndexMap[kNumRegs] = {
|
||||
|
||||
class StackHandlerConstants : public AllStatic {
|
||||
public:
|
||||
static const int kNextOffset = 0 * kPointerSize;
|
||||
static const int kStateOffset = 1 * kPointerSize;
|
||||
static const int kContextOffset = 2 * kPointerSize;
|
||||
static const int kFPOffset = 3 * kPointerSize;
|
||||
static const int kPCOffset = 4 * kPointerSize;
|
||||
static const int kNextOffset = 0 * kPointerSize;
|
||||
static const int kCodeOffset = 1 * kPointerSize;
|
||||
static const int kStateOffset = 2 * kPointerSize;
|
||||
static const int kContextOffset = 3 * kPointerSize;
|
||||
static const int kFPOffset = 4 * kPointerSize;
|
||||
|
||||
static const int kSize = kPCOffset + kPointerSize;
|
||||
static const int kSize = kFPOffset + kPointerSize;
|
||||
};
|
||||
|
||||
|
||||
|
@ -137,6 +137,8 @@ void FullCodeGenerator::Generate(CompilationInfo* info) {
|
||||
ASSERT(info_ == NULL);
|
||||
info_ = info;
|
||||
scope_ = info->scope();
|
||||
handler_table_ =
|
||||
isolate()->factory()->NewFixedArray(function()->handler_count(), TENURED);
|
||||
SetFunctionPosition(function());
|
||||
Comment cmnt(masm_, "[ function compiled by full code generator");
|
||||
|
||||
|
@ -2544,60 +2544,50 @@ void MacroAssembler::DebugBreak() {
|
||||
// Exception handling.
|
||||
|
||||
void MacroAssembler::PushTryHandler(CodeLocation try_location,
|
||||
HandlerType type) {
|
||||
HandlerType type,
|
||||
int handler_index) {
|
||||
// Adjust this code if not the case.
|
||||
STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kFPOffset == 3 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize);
|
||||
|
||||
// The return address is passed in register ra.
|
||||
// For the JSEntry handler, we must preserve a0-a3 and s0.
|
||||
// t1-t3 are available. We will build up the handler from the bottom by
|
||||
// pushing on the stack. First compute the state.
|
||||
unsigned state = StackHandler::OffsetField::encode(handler_index);
|
||||
if (try_location == IN_JAVASCRIPT) {
|
||||
if (type == TRY_CATCH_HANDLER) {
|
||||
li(t0, Operand(StackHandler::TRY_CATCH));
|
||||
} else {
|
||||
li(t0, Operand(StackHandler::TRY_FINALLY));
|
||||
}
|
||||
// Save the current handler as the next handler.
|
||||
li(t2, Operand(ExternalReference(Isolate::kHandlerAddress, isolate())));
|
||||
lw(t1, MemOperand(t2));
|
||||
|
||||
addiu(sp, sp, -StackHandlerConstants::kSize);
|
||||
sw(ra, MemOperand(sp, StackHandlerConstants::kPCOffset));
|
||||
sw(fp, MemOperand(sp, StackHandlerConstants::kFPOffset));
|
||||
sw(cp, MemOperand(sp, StackHandlerConstants::kContextOffset));
|
||||
sw(t0, MemOperand(sp, StackHandlerConstants::kStateOffset));
|
||||
sw(t1, MemOperand(sp, StackHandlerConstants::kNextOffset));
|
||||
|
||||
// Link this handler as the new current one.
|
||||
sw(sp, MemOperand(t2));
|
||||
|
||||
state |= (type == TRY_CATCH_HANDLER)
|
||||
? StackHandler::KindField::encode(StackHandler::TRY_CATCH)
|
||||
: StackHandler::KindField::encode(StackHandler::TRY_FINALLY);
|
||||
} else {
|
||||
// Must preserve a0-a3, and s0 (argv).
|
||||
ASSERT(try_location == IN_JS_ENTRY);
|
||||
// The frame pointer does not point to a JS frame so we save NULL
|
||||
// for fp. We expect the code throwing an exception to check fp
|
||||
// before dereferencing it to restore the context.
|
||||
li(t0, Operand(StackHandler::ENTRY));
|
||||
|
||||
// Save the current handler as the next handler.
|
||||
li(t2, Operand(ExternalReference(Isolate::kHandlerAddress, isolate())));
|
||||
lw(t1, MemOperand(t2));
|
||||
|
||||
ASSERT(Smi::FromInt(0) == 0); // Used for no context.
|
||||
|
||||
addiu(sp, sp, -StackHandlerConstants::kSize);
|
||||
sw(ra, MemOperand(sp, StackHandlerConstants::kPCOffset));
|
||||
sw(zero_reg, MemOperand(sp, StackHandlerConstants::kFPOffset));
|
||||
sw(zero_reg, MemOperand(sp, StackHandlerConstants::kContextOffset));
|
||||
sw(t0, MemOperand(sp, StackHandlerConstants::kStateOffset));
|
||||
sw(t1, MemOperand(sp, StackHandlerConstants::kNextOffset));
|
||||
|
||||
// Link this handler as the new current one.
|
||||
sw(sp, MemOperand(t2));
|
||||
state |= StackHandler::KindField::encode(StackHandler::ENTRY);
|
||||
}
|
||||
|
||||
// Set up the code object (t1) and the state (t2) for pushing.
|
||||
li(t1, Operand(CodeObject()));
|
||||
li(t2, Operand(state));
|
||||
|
||||
// Push the frame pointer, context, state, and code object.
|
||||
if (try_location == IN_JAVASCRIPT) {
|
||||
MultiPush(t1.bit() | t2.bit() | cp.bit() | fp.bit());
|
||||
} else {
|
||||
ASSERT_EQ(Smi::FromInt(0), 0);
|
||||
// The second zero_reg indicates no context.
|
||||
// The first zero_reg is the NULL frame pointer.
|
||||
// The operands are reversed to match the order of MultiPush/Pop.
|
||||
Push(zero_reg, zero_reg, t2, t1);
|
||||
}
|
||||
|
||||
// Link the current handler as the next handler.
|
||||
li(t2, Operand(ExternalReference(Isolate::kHandlerAddress, isolate())));
|
||||
lw(t1, MemOperand(t2));
|
||||
push(t1);
|
||||
// Set this new handler as the current one.
|
||||
sw(sp, MemOperand(t2));
|
||||
}
|
||||
|
||||
|
||||
@ -2610,19 +2600,36 @@ void MacroAssembler::PopTryHandler() {
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::Throw(Register value) {
|
||||
// v0 is expected to hold the exception.
|
||||
Move(v0, value);
|
||||
void MacroAssembler::JumpToHandlerEntry() {
|
||||
// Compute the handler entry address and jump to it. The handler table is
|
||||
// a fixed array of (smi-tagged) code offsets.
|
||||
// v0 = exception, a1 = code object, a2 = state.
|
||||
lw(a3, FieldMemOperand(a1, Code::kHandlerTableOffset)); // Handler table.
|
||||
Addu(a3, a3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
|
||||
srl(a2, a2, StackHandler::kKindWidth); // Handler index.
|
||||
sll(a2, a2, kPointerSizeLog2);
|
||||
Addu(a2, a3, a2);
|
||||
lw(a2, MemOperand(a2)); // Smi-tagged offset.
|
||||
Addu(a1, a1, Operand(Code::kHeaderSize - kHeapObjectTag)); // Code start.
|
||||
sra(t9, a2, kSmiTagSize);
|
||||
Addu(t9, t9, a1);
|
||||
Jump(t9); // Jump.
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::Throw(Register value) {
|
||||
// Adjust this code if not the case.
|
||||
STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kFPOffset == 3 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
|
||||
STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize);
|
||||
|
||||
// Drop the sp to the top of the handler.
|
||||
// The exception is expected in v0.
|
||||
Move(v0, value);
|
||||
|
||||
// Drop the stack pointer to the top of the top handler.
|
||||
li(a3, Operand(ExternalReference(Isolate::kHandlerAddress,
|
||||
isolate())));
|
||||
lw(sp, MemOperand(a3));
|
||||
@ -2631,44 +2638,19 @@ void MacroAssembler::Throw(Register value) {
|
||||
pop(a2);
|
||||
sw(a2, MemOperand(a3));
|
||||
|
||||
// Restore context and frame pointer, discard state (a3).
|
||||
MultiPop(a3.bit() | cp.bit() | fp.bit());
|
||||
// Get the code object (a1) and state (a2). Restore the context and frame
|
||||
// pointer.
|
||||
MultiPop(a1.bit() | a2.bit() | cp.bit() | fp.bit());
|
||||
|
||||
// If the handler is a JS frame, restore the context to the frame.
|
||||
// (a3 == ENTRY) == (fp == 0) == (cp == 0), so we could test any
|
||||
// of them.
|
||||
// (kind == ENTRY) == (fp == 0) == (cp == 0), so we could test either fp
|
||||
// or cp.
|
||||
Label done;
|
||||
Branch(&done, eq, fp, Operand(zero_reg));
|
||||
Branch(&done, eq, cp, Operand(zero_reg));
|
||||
sw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
|
||||
bind(&done);
|
||||
|
||||
#ifdef DEBUG
|
||||
// When emitting debug_code, set ra as return address for the jump.
|
||||
// 5 instructions: add: 1, pop: 2, jump: 2.
|
||||
const int kOffsetRaInstructions = 5;
|
||||
Label find_ra;
|
||||
|
||||
if (emit_debug_code()) {
|
||||
// Compute ra for the Jump(t9).
|
||||
const int kOffsetRaBytes = kOffsetRaInstructions * Assembler::kInstrSize;
|
||||
|
||||
// This branch-and-link sequence is needed to get the current PC on mips,
|
||||
// saved to the ra register. Then adjusted for instruction count.
|
||||
bal(&find_ra); // bal exposes branch-delay.
|
||||
nop(); // Branch delay slot nop.
|
||||
bind(&find_ra);
|
||||
addiu(ra, ra, kOffsetRaBytes);
|
||||
}
|
||||
#endif
|
||||
|
||||
pop(t9); // 2 instructions: lw, add sp.
|
||||
Jump(t9); // 2 instructions: jr, nop (in delay slot).
|
||||
|
||||
if (emit_debug_code()) {
|
||||
// Make sure that the expected number of instructions were generated.
|
||||
ASSERT_EQ(kOffsetRaInstructions,
|
||||
InstructionsGeneratedSince(&find_ra));
|
||||
}
|
||||
JumpToHandlerEntry();
|
||||
}
|
||||
|
||||
|
||||
@ -2677,10 +2659,10 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type,
|
||||
// Adjust this code if not the case.
|
||||
STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kStateOffset == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kContextOffset == 2 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kFPOffset == 3 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize);
|
||||
|
||||
// The exception is expected in v0.
|
||||
if (type == OUT_OF_MEMORY) {
|
||||
@ -2705,26 +2687,27 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type,
|
||||
li(a3, Operand(ExternalReference(Isolate::kHandlerAddress, isolate())));
|
||||
lw(sp, MemOperand(a3));
|
||||
|
||||
// Unwind the handlers until the top ENTRY handler is found.
|
||||
// Unwind the handlers until the ENTRY handler is found.
|
||||
Label fetch_next, check_kind;
|
||||
jmp(&check_kind);
|
||||
bind(&fetch_next);
|
||||
lw(sp, MemOperand(sp, StackHandlerConstants::kNextOffset));
|
||||
|
||||
bind(&check_kind);
|
||||
STATIC_ASSERT(StackHandler::ENTRY == 0);
|
||||
lw(a2, MemOperand(sp, StackHandlerConstants::kStateOffset));
|
||||
Branch(&fetch_next, ne, a2, Operand(StackHandler::ENTRY));
|
||||
And(a2, a2, Operand(StackHandler::KindField::kMask));
|
||||
Branch(&fetch_next, ne, a2, Operand(zero_reg));
|
||||
|
||||
// Set the top handler address to next handler past the top ENTRY handler.
|
||||
pop(a2);
|
||||
sw(a2, MemOperand(a3));
|
||||
|
||||
// Clear the context and frame pointer (0 was saved in the handler), and
|
||||
// discard the state (a2).
|
||||
MultiPop(a2.bit() | cp.bit() | fp.bit());
|
||||
// Get the code object (a1) and state (a2). Clear the context and frame
|
||||
// pointer (0 was saved in the handler).
|
||||
MultiPop(a1.bit() | a2.bit() | cp.bit() | fp.bit());
|
||||
|
||||
pop(t9); // 2 instructions: lw, add sp.
|
||||
Jump(t9); // 2 instructions: jr, nop (in delay slot).
|
||||
JumpToHandlerEntry();
|
||||
}
|
||||
|
||||
|
||||
|
@ -843,9 +843,9 @@ class MacroAssembler: public Assembler {
|
||||
// Exception handling.
|
||||
|
||||
// Push a new try handler and link into try handler chain.
|
||||
// The return address must be passed in register ra.
|
||||
// Clobber t0, t1, t2.
|
||||
void PushTryHandler(CodeLocation try_location, HandlerType type);
|
||||
void PushTryHandler(CodeLocation try_location,
|
||||
HandlerType type,
|
||||
int handler_index);
|
||||
|
||||
// Unlink the stack handler on top of the stack from the try handler chain.
|
||||
// Must preserve the result register.
|
||||
@ -1381,6 +1381,10 @@ class MacroAssembler: public Assembler {
|
||||
Register bitmap_reg,
|
||||
Register mask_reg);
|
||||
|
||||
// Helper for throwing exceptions. Compute a handler address and jump to
|
||||
// it. See the implementation for register usage.
|
||||
void JumpToHandlerEntry();
|
||||
|
||||
// Compute memory operands for safepoint stack slots.
|
||||
static int SafepointRegisterStackIndex(int reg_code);
|
||||
MemOperand SafepointRegisterSlot(Register reg);
|
||||
|
Loading…
Reference in New Issue
Block a user