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:
kmillikin@chromium.org 2011-11-21 14:05:18 +00:00
parent 561b5716d7
commit 29b5ceb897
6 changed files with 112 additions and 121 deletions

View File

@ -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);
}

View File

@ -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_);
}

View File

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

View File

@ -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");

View File

@ -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();
}

View File

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