Add a level of indirection to exception handler addresses.
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. R=vegorov@chromium.org,fschneider@chromium.org BUG= TEST= Review URL: http://codereview.chromium.org/8462010 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9975 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
5834284848
commit
1635117db9
@ -3712,7 +3712,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
|
||||
// r3: argc
|
||||
// [sp+0]: argv
|
||||
|
||||
Label invoke, exit;
|
||||
Label invoke, handler_entry, exit;
|
||||
|
||||
// Called from C, so do not pop argc and args on exit (preserve sp)
|
||||
// No need to save register-passed args
|
||||
@ -3775,23 +3775,26 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
|
||||
__ bind(&cont);
|
||||
__ push(ip);
|
||||
|
||||
// Call a faked try-block that does the invoke.
|
||||
__ bl(&invoke);
|
||||
|
||||
// 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.
|
||||
__ mov(ip, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
|
||||
isolate)));
|
||||
__ str(r0, MemOperand(ip));
|
||||
__ mov(r0, Operand(reinterpret_cast<int32_t>(Failure::Exception())));
|
||||
__ b(&exit);
|
||||
|
||||
// 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);
|
||||
// Must preserve r0-r4, r5-r7 are available.
|
||||
__ 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 bl(&invoke) above, which
|
||||
// restores all kCalleeSaved registers (including cp and fp) to their
|
||||
@ -4900,7 +4903,7 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void CallFunctionStub::FinishCode(Code* code) {
|
||||
void CallFunctionStub::FinishCode(Handle<Code> code) {
|
||||
code->set_has_function_cache(false);
|
||||
}
|
||||
|
||||
|
@ -136,7 +136,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_);
|
||||
}
|
||||
};
|
||||
@ -235,7 +235,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_);
|
||||
}
|
||||
|
@ -104,12 +104,12 @@ static const int kNumSafepointSavedRegisters =
|
||||
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 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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -127,6 +127,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");
|
||||
|
||||
|
@ -1167,46 +1167,48 @@ void MacroAssembler::DebugBreak() {
|
||||
|
||||
|
||||
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 pc (return address) is passed in register lr.
|
||||
// For the JSEntry handler, we must preserve r0-r4, r5-r7 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) {
|
||||
mov(r3, Operand(StackHandler::TRY_CATCH));
|
||||
state |= (type == TRY_CATCH_HANDLER)
|
||||
? StackHandler::KindField::encode(StackHandler::TRY_CATCH)
|
||||
: StackHandler::KindField::encode(StackHandler::TRY_FINALLY);
|
||||
} else {
|
||||
mov(r3, Operand(StackHandler::TRY_FINALLY));
|
||||
}
|
||||
stm(db_w, sp, r3.bit() | cp.bit() | fp.bit() | lr.bit());
|
||||
// Save the current handler as the next handler.
|
||||
mov(r3, Operand(ExternalReference(Isolate::kHandlerAddress, isolate())));
|
||||
ldr(r1, MemOperand(r3));
|
||||
push(r1);
|
||||
// Link this handler as the new current one.
|
||||
str(sp, MemOperand(r3));
|
||||
} else {
|
||||
// Must preserve r0-r4, r5-r7 are available.
|
||||
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.
|
||||
mov(r5, Operand(StackHandler::ENTRY)); // State.
|
||||
mov(r6, Operand(Smi::FromInt(0))); // Indicates no context.
|
||||
mov(r7, Operand(0, RelocInfo::NONE)); // NULL frame pointer.
|
||||
stm(db_w, sp, r5.bit() | r6.bit() | r7.bit() | lr.bit());
|
||||
// Save the current handler as the next handler.
|
||||
mov(r7, Operand(ExternalReference(Isolate::kHandlerAddress, isolate())));
|
||||
ldr(r6, MemOperand(r7));
|
||||
push(r6);
|
||||
// Link this handler as the new current one.
|
||||
str(sp, MemOperand(r7));
|
||||
state |= StackHandler::KindField::encode(StackHandler::ENTRY);
|
||||
}
|
||||
|
||||
// Set up the code object (r5) and the state (r6) for pushing.
|
||||
mov(r5, Operand(CodeObject()));
|
||||
mov(r6, Operand(state));
|
||||
|
||||
// Push the frame pointer, context, state, and code object.
|
||||
if (try_location == IN_JAVASCRIPT) {
|
||||
stm(db_w, sp, r5.bit() | r6.bit() | cp.bit() | fp.bit());
|
||||
} else {
|
||||
mov(r7, Operand(Smi::FromInt(0))); // Indicates no context.
|
||||
mov(ip, Operand(0, RelocInfo::NONE)); // NULL frame pointer.
|
||||
stm(db_w, sp, r5.bit() | r6.bit() | r7.bit() | ip.bit());
|
||||
}
|
||||
|
||||
// Link the current handler as the next handler.
|
||||
mov(r6, Operand(ExternalReference(Isolate::kHandlerAddress, isolate())));
|
||||
ldr(r5, MemOperand(r6));
|
||||
push(r5);
|
||||
// Set this new handler as the current one.
|
||||
str(sp, MemOperand(r6));
|
||||
}
|
||||
|
||||
|
||||
@ -1219,42 +1221,50 @@ void MacroAssembler::PopTryHandler() {
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::JumpToHandlerEntry() {
|
||||
// Compute the handler entry address and jump to it. The handler table is
|
||||
// a fixed array of (smi-tagged) code offsets.
|
||||
// r0 = exception, r1 = code object, r2 = state.
|
||||
ldr(r3, FieldMemOperand(r1, Code::kHandlerTableOffset)); // Handler table.
|
||||
add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
|
||||
mov(r2, Operand(r2, LSR, StackHandler::kKindWidth)); // Handler index.
|
||||
ldr(r2, MemOperand(r3, r2, LSL, kPointerSizeLog2)); // Smi-tagged offset.
|
||||
add(r1, r1, Operand(Code::kHeaderSize - kHeapObjectTag)); // Code start.
|
||||
add(pc, r1, Operand(r2, ASR, kSmiTagSize)); // 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);
|
||||
// r0 is expected to hold the exception.
|
||||
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);
|
||||
|
||||
// The exception is expected in r0.
|
||||
if (!value.is(r0)) {
|
||||
mov(r0, value);
|
||||
}
|
||||
|
||||
// Drop the sp to the top of the handler.
|
||||
// Drop the stack pointer to the top of the top handler.
|
||||
mov(r3, Operand(ExternalReference(Isolate::kHandlerAddress, isolate())));
|
||||
ldr(sp, MemOperand(r3));
|
||||
|
||||
// Restore the next handler.
|
||||
pop(r2);
|
||||
str(r2, MemOperand(r3));
|
||||
|
||||
// Restore context and frame pointer, discard state (r3).
|
||||
ldm(ia_w, sp, r3.bit() | cp.bit() | fp.bit());
|
||||
// Get the code object (r1) and state (r2). Restore the context and frame
|
||||
// pointer.
|
||||
ldm(ia_w, sp, r1.bit() | r2.bit() | cp.bit() | fp.bit());
|
||||
|
||||
// If the handler is a JS frame, restore the context to the frame.
|
||||
// (r3 == ENTRY) == (fp == 0) == (cp == 0), so we could test any
|
||||
// of them.
|
||||
cmp(r3, Operand(StackHandler::ENTRY));
|
||||
// (kind == ENTRY) == (fp == 0) == (cp == 0), so we could test either fp
|
||||
// or cp.
|
||||
tst(cp, cp);
|
||||
str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (emit_debug_code()) {
|
||||
mov(lr, Operand(pc));
|
||||
}
|
||||
#endif
|
||||
pop(pc);
|
||||
JumpToHandlerEntry();
|
||||
}
|
||||
|
||||
|
||||
@ -1263,10 +1273,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 r0.
|
||||
if (type == OUT_OF_MEMORY) {
|
||||
@ -1291,26 +1301,26 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type,
|
||||
mov(r3, Operand(ExternalReference(Isolate::kHandlerAddress, isolate())));
|
||||
ldr(sp, MemOperand(r3));
|
||||
|
||||
// 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);
|
||||
ldr(sp, MemOperand(sp, StackHandlerConstants::kNextOffset));
|
||||
|
||||
bind(&check_kind);
|
||||
STATIC_ASSERT(StackHandler::ENTRY == 0);
|
||||
ldr(r2, MemOperand(sp, StackHandlerConstants::kStateOffset));
|
||||
cmp(r2, Operand(StackHandler::ENTRY));
|
||||
tst(r2, Operand(StackHandler::KindField::kMask));
|
||||
b(ne, &fetch_next);
|
||||
|
||||
// Set the top handler address to next handler past the top ENTRY handler.
|
||||
pop(r2);
|
||||
str(r2, MemOperand(r3));
|
||||
// Get the code object (r1) and state (r2). Clear the context and frame
|
||||
// pointer (0 was saved in the handler).
|
||||
ldm(ia_w, sp, r1.bit() | r2.bit() | cp.bit() | fp.bit());
|
||||
|
||||
// Clear the context and frame pointer (0 was saved in the handler), and
|
||||
// discard the state (r2).
|
||||
ldm(ia_w, sp, r2.bit() | cp.bit() | fp.bit());
|
||||
|
||||
pop(pc);
|
||||
JumpToHandlerEntry();
|
||||
}
|
||||
|
||||
|
||||
|
@ -549,9 +549,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 lr.
|
||||
// On exit, r0 contains TOS (code slot).
|
||||
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.
|
||||
@ -1243,6 +1243,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);
|
||||
|
24
src/ast.h
24
src/ast.h
@ -835,18 +835,25 @@ class TargetCollector: public AstNode {
|
||||
|
||||
class TryStatement: public Statement {
|
||||
public:
|
||||
explicit TryStatement(Block* try_block)
|
||||
: try_block_(try_block), escaping_targets_(NULL) { }
|
||||
explicit TryStatement(int index, Block* try_block)
|
||||
: index_(index),
|
||||
try_block_(try_block),
|
||||
escaping_targets_(NULL) {
|
||||
}
|
||||
|
||||
void set_escaping_targets(ZoneList<Label*>* targets) {
|
||||
escaping_targets_ = targets;
|
||||
}
|
||||
|
||||
int index() const { return index_; }
|
||||
Block* try_block() const { return try_block_; }
|
||||
ZoneList<Label*>* escaping_targets() const { return escaping_targets_; }
|
||||
virtual bool IsInlineable() const;
|
||||
|
||||
private:
|
||||
// Unique (per-function) index of this handler. This is not an AST ID.
|
||||
int index_;
|
||||
|
||||
Block* try_block_;
|
||||
ZoneList<Label*>* escaping_targets_;
|
||||
};
|
||||
@ -854,11 +861,12 @@ class TryStatement: public Statement {
|
||||
|
||||
class TryCatchStatement: public TryStatement {
|
||||
public:
|
||||
TryCatchStatement(Block* try_block,
|
||||
TryCatchStatement(int index,
|
||||
Block* try_block,
|
||||
Scope* scope,
|
||||
Variable* variable,
|
||||
Block* catch_block)
|
||||
: TryStatement(try_block),
|
||||
: TryStatement(index, try_block),
|
||||
scope_(scope),
|
||||
variable_(variable),
|
||||
catch_block_(catch_block) {
|
||||
@ -880,8 +888,8 @@ class TryCatchStatement: public TryStatement {
|
||||
|
||||
class TryFinallyStatement: public TryStatement {
|
||||
public:
|
||||
TryFinallyStatement(Block* try_block, Block* finally_block)
|
||||
: TryStatement(try_block),
|
||||
TryFinallyStatement(int index, Block* try_block, Block* finally_block)
|
||||
: TryStatement(index, try_block),
|
||||
finally_block_(finally_block) { }
|
||||
|
||||
DECLARE_NODE_TYPE(TryFinallyStatement)
|
||||
@ -1644,6 +1652,7 @@ class FunctionLiteral: public Expression {
|
||||
ZoneList<Statement*>* body,
|
||||
int materialized_literal_count,
|
||||
int expected_property_count,
|
||||
int handler_count,
|
||||
bool has_only_simple_this_property_assignments,
|
||||
Handle<FixedArray> this_property_assignments,
|
||||
int parameter_count,
|
||||
@ -1657,6 +1666,7 @@ class FunctionLiteral: public Expression {
|
||||
inferred_name_(isolate->factory()->empty_string()),
|
||||
materialized_literal_count_(materialized_literal_count),
|
||||
expected_property_count_(expected_property_count),
|
||||
handler_count_(handler_count),
|
||||
parameter_count_(parameter_count),
|
||||
function_token_position_(RelocInfo::kNoPosition) {
|
||||
bitfield_ =
|
||||
@ -1684,6 +1694,7 @@ class FunctionLiteral: public Expression {
|
||||
|
||||
int materialized_literal_count() { return materialized_literal_count_; }
|
||||
int expected_property_count() { return expected_property_count_; }
|
||||
int handler_count() { return handler_count_; }
|
||||
bool has_only_simple_this_property_assignments() {
|
||||
return HasOnlySimpleThisPropertyAssignments::decode(bitfield_);
|
||||
}
|
||||
@ -1721,6 +1732,7 @@ class FunctionLiteral: public Expression {
|
||||
|
||||
int materialized_literal_count_;
|
||||
int expected_property_count_;
|
||||
int handler_count_;
|
||||
int parameter_count_;
|
||||
int function_token_position_;
|
||||
|
||||
|
@ -119,7 +119,7 @@ Handle<Code> CodeStub::GetCode() {
|
||||
Handle<Code> new_object = factory->NewCode(
|
||||
desc, flags, masm.CodeObject(), NeedsImmovableCode());
|
||||
RecordCodeGeneration(*new_object, &masm);
|
||||
FinishCode(*new_object);
|
||||
FinishCode(new_object);
|
||||
|
||||
// Update the dictionary and the root in Heap.
|
||||
Handle<NumberDictionary> dict =
|
||||
@ -213,6 +213,14 @@ void InstanceofStub::PrintName(StringStream* stream) {
|
||||
}
|
||||
|
||||
|
||||
void JSEntryStub::FinishCode(Handle<Code> code) {
|
||||
Handle<FixedArray> handler_table =
|
||||
code->GetIsolate()->factory()->NewFixedArray(1, TENURED);
|
||||
handler_table->set(0, Smi::FromInt(handler_offset_));
|
||||
code->set_handler_table(*handler_table);
|
||||
}
|
||||
|
||||
|
||||
void KeyedLoadElementStub::Generate(MacroAssembler* masm) {
|
||||
switch (elements_kind_) {
|
||||
case FAST_ELEMENTS:
|
||||
|
@ -175,7 +175,7 @@ class CodeStub BASE_EMBEDDED {
|
||||
void RecordCodeGeneration(Code* code, MacroAssembler* masm);
|
||||
|
||||
// Finish the code object after it has been generated.
|
||||
virtual void FinishCode(Code* code) { }
|
||||
virtual void FinishCode(Handle<Code> code) { }
|
||||
|
||||
// Activate newly generated stub. Is called after
|
||||
// registering stub in the stub cache.
|
||||
@ -441,7 +441,9 @@ class ICCompareStub: public CodeStub {
|
||||
class OpField: public BitField<int, 0, 3> { };
|
||||
class StateField: public BitField<int, 3, 5> { };
|
||||
|
||||
virtual void FinishCode(Code* code) { code->set_compare_state(state_); }
|
||||
virtual void FinishCode(Handle<Code> code) {
|
||||
code->set_compare_state(state_);
|
||||
}
|
||||
|
||||
virtual CodeStub::Major MajorKey() { return CompareIC; }
|
||||
virtual int MinorKey();
|
||||
@ -544,7 +546,7 @@ class CompareStub: public CodeStub {
|
||||
int MinorKey();
|
||||
|
||||
virtual int GetCodeKind() { return Code::COMPARE_IC; }
|
||||
virtual void FinishCode(Code* code) {
|
||||
virtual void FinishCode(Handle<Code> code) {
|
||||
code->set_compare_state(CompareIC::GENERIC);
|
||||
}
|
||||
|
||||
@ -609,6 +611,10 @@ class JSEntryStub : public CodeStub {
|
||||
private:
|
||||
Major MajorKey() { return JSEntry; }
|
||||
int MinorKey() { return 0; }
|
||||
|
||||
virtual void FinishCode(Handle<Code> code);
|
||||
|
||||
int handler_offset_;
|
||||
};
|
||||
|
||||
|
||||
@ -685,7 +691,7 @@ class CallFunctionStub: public CodeStub {
|
||||
|
||||
void Generate(MacroAssembler* masm);
|
||||
|
||||
virtual void FinishCode(Code* code);
|
||||
virtual void FinishCode(Handle<Code> code);
|
||||
|
||||
static void Clear(Heap* heap, Address address);
|
||||
|
||||
@ -996,7 +1002,7 @@ class ToBooleanStub: public CodeStub {
|
||||
Major MajorKey() { return ToBoolean; }
|
||||
int MinorKey() { return (tos_.code() << NUMBER_OF_TYPES) | types_.ToByte(); }
|
||||
|
||||
virtual void FinishCode(Code* code) {
|
||||
virtual void FinishCode(Handle<Code> code) {
|
||||
code->set_to_boolean_state(types_.ToByte());
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ inline bool StackHandler::includes(Address address) const {
|
||||
|
||||
inline void StackHandler::Iterate(ObjectVisitor* v, Code* holder) const {
|
||||
v->VisitPointer(context_address());
|
||||
StackFrame::IteratePc(v, pc_address(), holder);
|
||||
v->VisitPointer(code_address());
|
||||
}
|
||||
|
||||
|
||||
@ -78,23 +78,23 @@ inline StackHandler* StackHandler::FromAddress(Address address) {
|
||||
|
||||
|
||||
inline bool StackHandler::is_entry() const {
|
||||
return state() == ENTRY;
|
||||
return kind() == ENTRY;
|
||||
}
|
||||
|
||||
|
||||
inline bool StackHandler::is_try_catch() const {
|
||||
return state() == TRY_CATCH;
|
||||
return kind() == TRY_CATCH;
|
||||
}
|
||||
|
||||
|
||||
inline bool StackHandler::is_try_finally() const {
|
||||
return state() == TRY_FINALLY;
|
||||
return kind() == TRY_FINALLY;
|
||||
}
|
||||
|
||||
|
||||
inline StackHandler::State StackHandler::state() const {
|
||||
inline StackHandler::Kind StackHandler::kind() const {
|
||||
const int offset = StackHandlerConstants::kStateOffset;
|
||||
return static_cast<State>(Memory::int_at(address() + offset));
|
||||
return KindField::decode(Memory::unsigned_at(address() + offset));
|
||||
}
|
||||
|
||||
|
||||
@ -104,9 +104,9 @@ inline Object** StackHandler::context_address() const {
|
||||
}
|
||||
|
||||
|
||||
inline Address* StackHandler::pc_address() const {
|
||||
const int offset = StackHandlerConstants::kPCOffset;
|
||||
return reinterpret_cast<Address*>(address() + offset);
|
||||
inline Object** StackHandler::code_address() const {
|
||||
const int offset = StackHandlerConstants::kCodeOffset;
|
||||
return reinterpret_cast<Object**>(address() + offset);
|
||||
}
|
||||
|
||||
|
||||
|
11
src/frames.h
11
src/frames.h
@ -84,12 +84,17 @@ class InnerPointerToCodeCache {
|
||||
|
||||
class StackHandler BASE_EMBEDDED {
|
||||
public:
|
||||
enum State {
|
||||
enum Kind {
|
||||
ENTRY,
|
||||
TRY_CATCH,
|
||||
TRY_FINALLY
|
||||
};
|
||||
|
||||
static const int kKindWidth = 2;
|
||||
static const int kOffsetWidth = 32 - kKindWidth;
|
||||
class KindField: public BitField<StackHandler::Kind, 0, kKindWidth> {};
|
||||
class OffsetField: public BitField<unsigned, kKindWidth, kOffsetWidth> {};
|
||||
|
||||
// Get the address of this stack handler.
|
||||
inline Address address() const;
|
||||
|
||||
@ -112,10 +117,10 @@ class StackHandler BASE_EMBEDDED {
|
||||
|
||||
private:
|
||||
// Accessors.
|
||||
inline State state() const;
|
||||
inline Kind kind() const;
|
||||
|
||||
inline Object** context_address() const;
|
||||
inline Address* pc_address() const;
|
||||
inline Object** code_address() const;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(StackHandler);
|
||||
};
|
||||
|
@ -286,6 +286,7 @@ bool FullCodeGenerator::MakeCode(CompilationInfo* info) {
|
||||
code->set_optimizable(info->IsOptimizable());
|
||||
cgen.PopulateDeoptimizationData(code);
|
||||
code->set_has_deoptimization_support(info->HasDeoptimizationSupport());
|
||||
code->set_handler_table(*cgen.handler_table());
|
||||
#ifdef ENABLE_DEBUGGER_SUPPORT
|
||||
code->set_has_debug_break_slots(
|
||||
info->isolate()->debugger()->IsDebuggerActive());
|
||||
@ -1086,20 +1087,17 @@ void FullCodeGenerator::VisitForStatement(ForStatement* stmt) {
|
||||
void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
|
||||
Comment cmnt(masm_, "[ TryCatchStatement");
|
||||
SetStatementPosition(stmt);
|
||||
// The try block adds a handler to the exception handler chain
|
||||
// before entering, and removes it again when exiting normally.
|
||||
// If an exception is thrown during execution of the try block,
|
||||
// control is passed to the handler, which also consumes the handler.
|
||||
// At this point, the exception is in a register, and store it in
|
||||
// the temporary local variable (prints as ".catch-var") before
|
||||
// executing the catch block. The catch block has been rewritten
|
||||
// to introduce a new scope to bind the catch variable and to remove
|
||||
// that scope again afterwards.
|
||||
|
||||
Label try_handler_setup, done;
|
||||
__ Call(&try_handler_setup);
|
||||
// Try handler code, exception in result register.
|
||||
// The try block adds a handler to the exception handler chain before
|
||||
// entering, and removes it again when exiting normally. If an exception
|
||||
// is thrown during execution of the try block, the handler is consumed
|
||||
// and control is passed to the catch block with the exception in the
|
||||
// result register.
|
||||
|
||||
Label try_entry, handler_entry, exit;
|
||||
__ jmp(&try_entry);
|
||||
__ bind(&handler_entry);
|
||||
handler_table()->set(stmt->index(), Smi::FromInt(handler_entry.pos()));
|
||||
// Exception handler code, the exception is in the result register.
|
||||
// Extend the context before executing the catch block.
|
||||
{ Comment cmnt(masm_, "[ Extend catch context");
|
||||
__ Push(stmt->variable()->name());
|
||||
@ -1113,24 +1111,23 @@ void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
|
||||
Scope* saved_scope = scope();
|
||||
scope_ = stmt->scope();
|
||||
ASSERT(scope_->declarations()->is_empty());
|
||||
{ WithOrCatch body(this);
|
||||
{ WithOrCatch catch_body(this);
|
||||
Visit(stmt->catch_block());
|
||||
}
|
||||
// Restore the context.
|
||||
LoadContextField(context_register(), Context::PREVIOUS_INDEX);
|
||||
StoreToFrameField(StandardFrameConstants::kContextOffset, context_register());
|
||||
scope_ = saved_scope;
|
||||
__ jmp(&done);
|
||||
__ jmp(&exit);
|
||||
|
||||
// Try block code. Sets up the exception handler chain.
|
||||
__ bind(&try_handler_setup);
|
||||
{
|
||||
TryCatch try_block(this);
|
||||
__ PushTryHandler(IN_JAVASCRIPT, TRY_CATCH_HANDLER);
|
||||
__ bind(&try_entry);
|
||||
__ PushTryHandler(IN_JAVASCRIPT, TRY_CATCH_HANDLER, stmt->index());
|
||||
{ TryCatch try_body(this);
|
||||
Visit(stmt->try_block());
|
||||
__ PopTryHandler();
|
||||
}
|
||||
__ bind(&done);
|
||||
__ PopTryHandler();
|
||||
__ bind(&exit);
|
||||
}
|
||||
|
||||
|
||||
@ -1147,7 +1144,7 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
||||
// (break/continue/return). The site of the, e.g., break removes the
|
||||
// try handler and calls the finally block code before continuing
|
||||
// its outward control transfer.
|
||||
// 3. by exiting the try-block with a thrown exception.
|
||||
// 3. By exiting the try-block with a thrown exception.
|
||||
// This can happen in nested function calls. It traverses the try-handler
|
||||
// chain and consumes the try-handler entry before jumping to the
|
||||
// handler code. The handler code then calls the finally-block before
|
||||
@ -1158,44 +1155,39 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
||||
// exception) in the result register (rax/eax/r0), both of which must
|
||||
// be preserved. The return address isn't GC-safe, so it should be
|
||||
// cooked before GC.
|
||||
Label finally_entry;
|
||||
Label try_handler_setup;
|
||||
Label try_entry, handler_entry, finally_entry;
|
||||
|
||||
// Setup the try-handler chain. Use a call to
|
||||
// Jump to try-handler setup and try-block code. Use call to put try-handler
|
||||
// address on stack.
|
||||
__ Call(&try_handler_setup);
|
||||
// Try handler code. Return address of call is pushed on handler stack.
|
||||
{
|
||||
// This code is only executed during stack-handler traversal when an
|
||||
// exception is thrown. The exception is in the result register, which
|
||||
// is retained by the finally block.
|
||||
// Call the finally block and then rethrow the exception if it returns.
|
||||
// Jump to try-handler setup and try-block code.
|
||||
__ jmp(&try_entry);
|
||||
__ bind(&handler_entry);
|
||||
handler_table()->set(stmt->index(), Smi::FromInt(handler_entry.pos()));
|
||||
// Exception handler code. This code is only executed when an exception
|
||||
// is thrown. The exception is in the result register, and must be
|
||||
// preserved by the finally block. Call the finally block and then
|
||||
// rethrow the exception if it returns.
|
||||
__ Call(&finally_entry);
|
||||
__ push(result_register());
|
||||
__ CallRuntime(Runtime::kReThrow, 1);
|
||||
}
|
||||
|
||||
__ bind(&finally_entry);
|
||||
{
|
||||
// Finally block implementation.
|
||||
Finally finally_block(this);
|
||||
__ bind(&finally_entry);
|
||||
EnterFinallyBlock();
|
||||
{ Finally finally_body(this);
|
||||
Visit(stmt->finally_block());
|
||||
}
|
||||
ExitFinallyBlock(); // Return to the calling code.
|
||||
}
|
||||
|
||||
__ bind(&try_handler_setup);
|
||||
{
|
||||
// Setup try handler (stack pointer registers).
|
||||
TryFinally try_block(this, &finally_entry);
|
||||
__ PushTryHandler(IN_JAVASCRIPT, TRY_FINALLY_HANDLER);
|
||||
// Setup try handler.
|
||||
__ bind(&try_entry);
|
||||
__ PushTryHandler(IN_JAVASCRIPT, TRY_FINALLY_HANDLER, stmt->index());
|
||||
{ TryFinally try_body(this, &finally_entry);
|
||||
Visit(stmt->try_block());
|
||||
__ PopTryHandler();
|
||||
}
|
||||
__ PopTryHandler();
|
||||
// Execute the finally block on the way out. Clobber the unpredictable
|
||||
// value in the accumulator with one that's safe for GC. The finally
|
||||
// block will unconditionally preserve the accumulator on the stack.
|
||||
// value in the result register with one that's safe for GC because the
|
||||
// finally block will unconditionally preserve the result register on the
|
||||
// stack.
|
||||
ClearAccumulator();
|
||||
__ Call(&finally_entry);
|
||||
}
|
||||
|
@ -93,6 +93,8 @@ class FullCodeGenerator: public AstVisitor {
|
||||
void Generate(CompilationInfo* info);
|
||||
void PopulateDeoptimizationData(Handle<Code> code);
|
||||
|
||||
Handle<FixedArray> handler_table() { return handler_table_; }
|
||||
|
||||
class StateField : public BitField<State, 0, 8> { };
|
||||
class PcField : public BitField<unsigned, 8, 32-8> { };
|
||||
|
||||
@ -755,6 +757,7 @@ class FullCodeGenerator: public AstVisitor {
|
||||
const ExpressionContext* context_;
|
||||
ZoneList<BailoutEntry> bailout_entries_;
|
||||
ZoneList<BailoutEntry> stack_checks_;
|
||||
Handle<FixedArray> handler_table_;
|
||||
|
||||
friend class NestedStatement;
|
||||
|
||||
|
10
src/heap.cc
10
src/heap.cc
@ -3157,10 +3157,9 @@ MaybeObject* Heap::CreateCode(const CodeDesc& desc,
|
||||
bool immovable) {
|
||||
// Allocate ByteArray before the Code object, so that we do not risk
|
||||
// leaving uninitialized Code object (and breaking the heap).
|
||||
Object* reloc_info;
|
||||
{ MaybeObject* maybe_reloc_info = AllocateByteArray(desc.reloc_size, TENURED);
|
||||
if (!maybe_reloc_info->ToObject(&reloc_info)) return maybe_reloc_info;
|
||||
}
|
||||
ByteArray* reloc_info;
|
||||
MaybeObject* maybe_reloc_info = AllocateByteArray(desc.reloc_size, TENURED);
|
||||
if (!maybe_reloc_info->To(&reloc_info)) return maybe_reloc_info;
|
||||
|
||||
// Compute size.
|
||||
int body_size = RoundUp(desc.instr_size, kObjectAlignment);
|
||||
@ -3184,12 +3183,13 @@ MaybeObject* Heap::CreateCode(const CodeDesc& desc,
|
||||
ASSERT(!isolate_->code_range()->exists() ||
|
||||
isolate_->code_range()->contains(code->address()));
|
||||
code->set_instruction_size(desc.instr_size);
|
||||
code->set_relocation_info(ByteArray::cast(reloc_info));
|
||||
code->set_relocation_info(reloc_info);
|
||||
code->set_flags(flags);
|
||||
if (code->is_call_stub() || code->is_keyed_call_stub()) {
|
||||
code->set_check_type(RECEIVER_MAP_CHECK);
|
||||
}
|
||||
code->set_deoptimization_data(empty_fixed_array());
|
||||
code->set_handler_table(empty_fixed_array());
|
||||
code->set_next_code_flushing_candidate(undefined_value());
|
||||
// Allow self references to created code object by patching the handle to
|
||||
// point to the newly allocated Code object.
|
||||
|
@ -4337,7 +4337,7 @@ void StackCheckStub::Generate(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void CallFunctionStub::FinishCode(Code* code) {
|
||||
void CallFunctionStub::FinishCode(Handle<Code> code) {
|
||||
code->set_has_function_cache(RecordCallTarget());
|
||||
}
|
||||
|
||||
@ -4715,7 +4715,7 @@ void CEntryStub::Generate(MacroAssembler* masm) {
|
||||
|
||||
|
||||
void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
|
||||
Label invoke, exit;
|
||||
Label invoke, handler_entry, exit;
|
||||
Label not_outermost_js, not_outermost_js_2;
|
||||
|
||||
// Setup frame.
|
||||
@ -4748,20 +4748,23 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
|
||||
__ push(Immediate(Smi::FromInt(StackFrame::INNER_JSENTRY_FRAME)));
|
||||
__ bind(&cont);
|
||||
|
||||
// Call a faked try-block that does the invoke.
|
||||
__ call(&invoke);
|
||||
|
||||
// Caught exception: Store result (exception) in the pending
|
||||
// exception field in the JSEnv and return a failure sentinel.
|
||||
// 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.
|
||||
ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
|
||||
masm->isolate());
|
||||
__ mov(Operand::StaticVariable(pending_exception), eax);
|
||||
__ mov(eax, reinterpret_cast<int32_t>(Failure::Exception()));
|
||||
__ jmp(&exit);
|
||||
|
||||
// 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);
|
||||
|
||||
// Clear any pending exceptions.
|
||||
__ mov(edx, Immediate(masm->isolate()->factory()->the_hole_value()));
|
||||
@ -4770,13 +4773,12 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
|
||||
// Fake a receiver (NULL).
|
||||
__ push(Immediate(0)); // receiver
|
||||
|
||||
// Invoke the function by calling through JS entry trampoline
|
||||
// builtin and pop the faked function when we return. Notice that we
|
||||
// cannot store a reference to the trampoline code directly in this
|
||||
// stub, because the builtin stubs may not have been generated yet.
|
||||
// Invoke the function by calling through JS entry trampoline builtin and
|
||||
// pop the faked function when we return. Notice that we cannot store a
|
||||
// reference to the trampoline code directly in this stub, because the
|
||||
// builtin stubs may not have been generated yet.
|
||||
if (is_construct) {
|
||||
ExternalReference construct_entry(
|
||||
Builtins::kJSConstructEntryTrampoline,
|
||||
ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline,
|
||||
masm->isolate());
|
||||
__ mov(edx, Immediate(construct_entry));
|
||||
} else {
|
||||
|
@ -147,7 +147,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_);
|
||||
}
|
||||
};
|
||||
@ -234,7 +234,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_);
|
||||
}
|
||||
|
@ -59,12 +59,12 @@ static const int kNumSafepointRegisters = 8;
|
||||
class StackHandlerConstants : public AllStatic {
|
||||
public:
|
||||
static const int kNextOffset = 0 * kPointerSize;
|
||||
static const int kContextOffset = 1 * kPointerSize;
|
||||
static const int kFPOffset = 2 * kPointerSize;
|
||||
static const int kStateOffset = 3 * kPointerSize;
|
||||
static const int kPCOffset = 4 * 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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -117,6 +117,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");
|
||||
|
||||
|
@ -725,84 +725,105 @@ void MacroAssembler::LeaveApiExitFrame() {
|
||||
|
||||
|
||||
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);
|
||||
STATIC_ASSERT(StackHandlerConstants::kContextOffset == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kStateOffset == 3 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize);
|
||||
// The pc (return address) is already on TOS.
|
||||
STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize);
|
||||
|
||||
// We will build up the handler from the bottom by pushing on the stack.
|
||||
// First compute the state and push the frame pointer and context.
|
||||
unsigned state = StackHandler::OffsetField::encode(handler_index);
|
||||
if (try_location == IN_JAVASCRIPT) {
|
||||
if (type == TRY_CATCH_HANDLER) {
|
||||
push(Immediate(StackHandler::TRY_CATCH));
|
||||
} else {
|
||||
push(Immediate(StackHandler::TRY_FINALLY));
|
||||
}
|
||||
push(ebp);
|
||||
push(esi);
|
||||
state |= (type == TRY_CATCH_HANDLER)
|
||||
? StackHandler::KindField::encode(StackHandler::TRY_CATCH)
|
||||
: StackHandler::KindField::encode(StackHandler::TRY_FINALLY);
|
||||
} else {
|
||||
ASSERT(try_location == IN_JS_ENTRY);
|
||||
// The frame pointer does not point to a JS frame so we save NULL
|
||||
// for ebp. We expect the code throwing an exception to check ebp
|
||||
// before dereferencing it to restore the context.
|
||||
push(Immediate(StackHandler::ENTRY));
|
||||
// The frame pointer does not point to a JS frame so we save NULL for
|
||||
// ebp. We expect the code throwing an exception to check ebp before
|
||||
// dereferencing it to restore the context.
|
||||
push(Immediate(0)); // NULL frame pointer.
|
||||
push(Immediate(Smi::FromInt(0))); // No context.
|
||||
state |= StackHandler::KindField::encode(StackHandler::ENTRY);
|
||||
}
|
||||
// Save the current handler as the next handler.
|
||||
push(Operand::StaticVariable(ExternalReference(Isolate::kHandlerAddress,
|
||||
isolate())));
|
||||
// Link this handler as the new current one.
|
||||
mov(Operand::StaticVariable(ExternalReference(Isolate::kHandlerAddress,
|
||||
isolate())),
|
||||
esp);
|
||||
|
||||
// Push the state and the code object.
|
||||
push(Immediate(state));
|
||||
push(CodeObject());
|
||||
|
||||
// Link the current handler as the next handler.
|
||||
ExternalReference handler_address(Isolate::kHandlerAddress, isolate());
|
||||
push(Operand::StaticVariable(handler_address));
|
||||
// Set this new handler as the current one.
|
||||
mov(Operand::StaticVariable(handler_address), esp);
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::PopTryHandler() {
|
||||
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
|
||||
pop(Operand::StaticVariable(ExternalReference(Isolate::kHandlerAddress,
|
||||
isolate())));
|
||||
ExternalReference handler_address(Isolate::kHandlerAddress, isolate());
|
||||
pop(Operand::StaticVariable(handler_address));
|
||||
add(esp, Immediate(StackHandlerConstants::kSize - kPointerSize));
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::JumpToHandlerEntry() {
|
||||
// Compute the handler entry address and jump to it. The handler table is
|
||||
// a fixed array of (smi-tagged) code offsets.
|
||||
// eax = exception, edi = code object, edx = state.
|
||||
mov(ebx, FieldOperand(edi, Code::kHandlerTableOffset));
|
||||
shr(edx, StackHandler::kKindWidth);
|
||||
mov(edx, FieldOperand(ebx, edx, times_4, FixedArray::kHeaderSize));
|
||||
SmiUntag(edx);
|
||||
lea(edi, FieldOperand(edi, edx, times_1, Code::kHeaderSize));
|
||||
jmp(edi);
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::Throw(Register value) {
|
||||
// Adjust this code if not the case.
|
||||
STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
|
||||
STATIC_ASSERT(StackHandlerConstants::kContextOffset == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kStateOffset == 3 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize);
|
||||
// eax must hold the exception.
|
||||
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 eax.
|
||||
if (!value.is(eax)) {
|
||||
mov(eax, value);
|
||||
}
|
||||
|
||||
// Drop the sp to the top of the handler.
|
||||
ExternalReference handler_address(Isolate::kHandlerAddress,
|
||||
isolate());
|
||||
// Drop the stack pointer to the top of the top handler.
|
||||
ExternalReference handler_address(Isolate::kHandlerAddress, isolate());
|
||||
mov(esp, Operand::StaticVariable(handler_address));
|
||||
|
||||
// Restore next handler, context, and frame pointer; discard handler state.
|
||||
// Restore the next handler.
|
||||
pop(Operand::StaticVariable(handler_address));
|
||||
|
||||
// Remove the code object and state, compute the handler address in edi.
|
||||
pop(edi); // Code object.
|
||||
pop(edx); // Index and state.
|
||||
|
||||
// Restore the context and frame pointer.
|
||||
pop(esi); // Context.
|
||||
pop(ebp); // Frame pointer.
|
||||
pop(edx); // State.
|
||||
|
||||
// If the handler is a JS frame, restore the context to the frame.
|
||||
// (edx == ENTRY) == (ebp == 0) == (esi == 0), so we could test any
|
||||
// of them.
|
||||
// (kind == ENTRY) == (ebp == 0) == (esi == 0), so we could test either
|
||||
// ebp or esi.
|
||||
Label skip;
|
||||
cmp(edx, Immediate(StackHandler::ENTRY));
|
||||
j(equal, &skip, Label::kNear);
|
||||
test(esi, esi);
|
||||
j(zero, &skip, Label::kNear);
|
||||
mov(Operand(ebp, StandardFrameConstants::kContextOffset), esi);
|
||||
bind(&skip);
|
||||
|
||||
ret(0);
|
||||
JumpToHandlerEntry();
|
||||
}
|
||||
|
||||
|
||||
@ -811,10 +832,10 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type,
|
||||
// Adjust this code if not the case.
|
||||
STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
|
||||
STATIC_ASSERT(StackHandlerConstants::kContextOffset == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kStateOffset == 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 eax.
|
||||
if (type == OUT_OF_MEMORY) {
|
||||
@ -843,20 +864,23 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type,
|
||||
mov(esp, Operand(esp, StackHandlerConstants::kNextOffset));
|
||||
|
||||
bind(&check_kind);
|
||||
cmp(Operand(esp, StackHandlerConstants::kStateOffset),
|
||||
Immediate(StackHandler::ENTRY));
|
||||
j(not_equal, &fetch_next);
|
||||
STATIC_ASSERT(StackHandler::ENTRY == 0);
|
||||
test(Operand(esp, StackHandlerConstants::kStateOffset),
|
||||
Immediate(StackHandler::KindField::kMask));
|
||||
j(not_zero, &fetch_next);
|
||||
|
||||
// Set the top handler address to next handler past the top ENTRY handler.
|
||||
pop(Operand::StaticVariable(handler_address));
|
||||
|
||||
// Clear the context and frame pointer (0 was saved in the handler), and
|
||||
// discard the state.
|
||||
// Remove the code object and state, compute the handler address in edi.
|
||||
pop(edi); // Code object.
|
||||
pop(edx); // Index and state.
|
||||
|
||||
// Clear the context pointer and frame pointer (0 was saved in the handler).
|
||||
pop(esi);
|
||||
pop(ebp);
|
||||
pop(edx); // State.
|
||||
|
||||
ret(0);
|
||||
JumpToHandlerEntry();
|
||||
}
|
||||
|
||||
|
||||
|
@ -451,9 +451,10 @@ class MacroAssembler: public Assembler {
|
||||
// ---------------------------------------------------------------------------
|
||||
// Exception handling
|
||||
|
||||
// Push a new try handler and link into try handler chain. The return
|
||||
// address must be pushed before calling this helper.
|
||||
void PushTryHandler(CodeLocation try_location, HandlerType type);
|
||||
// Push a new try handler and link it into try handler chain.
|
||||
void PushTryHandler(CodeLocation try_location,
|
||||
HandlerType type,
|
||||
int handler_index);
|
||||
|
||||
// Unlink the stack handler on top of the stack from the try handler chain.
|
||||
void PopTryHandler();
|
||||
@ -842,6 +843,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.
|
||||
Operand SafepointRegisterSlot(Register reg);
|
||||
static int SafepointRegisterStackIndex(int reg_code);
|
||||
|
@ -3883,6 +3883,7 @@ JSMessageObject* JSMessageObject::cast(Object* obj) {
|
||||
|
||||
INT_ACCESSORS(Code, instruction_size, kInstructionSizeOffset)
|
||||
ACCESSORS(Code, relocation_info, ByteArray, kRelocationInfoOffset)
|
||||
ACCESSORS(Code, handler_table, FixedArray, kHandlerTableOffset)
|
||||
ACCESSORS(Code, deoptimization_data, FixedArray, kDeoptimizationDataOffset)
|
||||
ACCESSORS(Code, next_code_flushing_candidate,
|
||||
Object, kNextCodeFlushingCandidateOffset)
|
||||
|
@ -127,6 +127,9 @@ void Code::CodeIterateBody(Heap* heap) {
|
||||
StaticVisitor::VisitPointer(
|
||||
heap,
|
||||
reinterpret_cast<Object**>(this->address() + kRelocationInfoOffset));
|
||||
StaticVisitor::VisitPointer(
|
||||
heap,
|
||||
reinterpret_cast<Object**>(this->address() + kHandlerTableOffset));
|
||||
StaticVisitor::VisitPointer(
|
||||
heap,
|
||||
reinterpret_cast<Object**>(this->address() + kDeoptimizationDataOffset));
|
||||
|
@ -3829,6 +3829,9 @@ class Code: public HeapObject {
|
||||
DECL_ACCESSORS(relocation_info, ByteArray)
|
||||
void InvalidateRelocation();
|
||||
|
||||
// [handler_table]: Fixed array containing offsets of exception handlers.
|
||||
DECL_ACCESSORS(handler_table, FixedArray)
|
||||
|
||||
// [deoptimization_data]: Array containing data for deopt.
|
||||
DECL_ACCESSORS(deoptimization_data, FixedArray)
|
||||
|
||||
@ -4057,8 +4060,9 @@ class Code: public HeapObject {
|
||||
// Layout description.
|
||||
static const int kInstructionSizeOffset = HeapObject::kHeaderSize;
|
||||
static const int kRelocationInfoOffset = kInstructionSizeOffset + kIntSize;
|
||||
static const int kHandlerTableOffset = kRelocationInfoOffset + kPointerSize;
|
||||
static const int kDeoptimizationDataOffset =
|
||||
kRelocationInfoOffset + kPointerSize;
|
||||
kHandlerTableOffset + kPointerSize;
|
||||
static const int kNextCodeFlushingCandidateOffset =
|
||||
kDeoptimizationDataOffset + kPointerSize;
|
||||
static const int kFlagsOffset =
|
||||
|
@ -493,6 +493,9 @@ class Parser::FunctionState BASE_EMBEDDED {
|
||||
return next_materialized_literal_index_ - JSFunction::kLiteralsPrefixSize;
|
||||
}
|
||||
|
||||
int NextHandlerIndex() { return next_handler_index_++; }
|
||||
int handler_count() { return next_handler_index_; }
|
||||
|
||||
void SetThisPropertyAssignmentInfo(
|
||||
bool only_simple_this_property_assignments,
|
||||
Handle<FixedArray> this_property_assignments) {
|
||||
@ -516,6 +519,9 @@ class Parser::FunctionState BASE_EMBEDDED {
|
||||
// array literals.
|
||||
int next_materialized_literal_index_;
|
||||
|
||||
// Used to assign a per-function index to try and catch handlers.
|
||||
int next_handler_index_;
|
||||
|
||||
// Properties count estimation.
|
||||
int expected_property_count_;
|
||||
|
||||
@ -535,6 +541,7 @@ Parser::FunctionState::FunctionState(Parser* parser,
|
||||
Scope* scope,
|
||||
Isolate* isolate)
|
||||
: next_materialized_literal_index_(JSFunction::kLiteralsPrefixSize),
|
||||
next_handler_index_(0),
|
||||
expected_property_count_(0),
|
||||
only_simple_this_property_assignments_(false),
|
||||
this_property_assignments_(isolate->factory()->empty_fixed_array()),
|
||||
@ -589,10 +596,10 @@ Parser::Parser(Handle<Script> script,
|
||||
top_scope_(NULL),
|
||||
current_function_state_(NULL),
|
||||
target_stack_(NULL),
|
||||
allow_natives_syntax_(allow_natives_syntax),
|
||||
extension_(extension),
|
||||
pre_data_(pre_data),
|
||||
fni_(NULL),
|
||||
allow_natives_syntax_(allow_natives_syntax),
|
||||
stack_overflow_(false),
|
||||
parenthesized_function_(false),
|
||||
harmony_scoping_(false) {
|
||||
@ -669,6 +676,7 @@ FunctionLiteral* Parser::DoParseProgram(Handle<String> source,
|
||||
body,
|
||||
function_state.materialized_literal_count(),
|
||||
function_state.expected_property_count(),
|
||||
function_state.handler_count(),
|
||||
function_state.only_simple_this_property_assignments(),
|
||||
function_state.this_property_assignments(),
|
||||
0,
|
||||
@ -2316,8 +2324,9 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
|
||||
if (catch_block != NULL && finally_block != NULL) {
|
||||
// If we have both, create an inner try/catch.
|
||||
ASSERT(catch_scope != NULL && catch_variable != NULL);
|
||||
TryCatchStatement* statement =
|
||||
new(zone()) TryCatchStatement(try_block,
|
||||
int index = current_function_state_->NextHandlerIndex();
|
||||
TryCatchStatement* statement = new(zone()) TryCatchStatement(index,
|
||||
try_block,
|
||||
catch_scope,
|
||||
catch_variable,
|
||||
catch_block);
|
||||
@ -2331,14 +2340,18 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
|
||||
if (catch_block != NULL) {
|
||||
ASSERT(finally_block == NULL);
|
||||
ASSERT(catch_scope != NULL && catch_variable != NULL);
|
||||
result =
|
||||
new(zone()) TryCatchStatement(try_block,
|
||||
int index = current_function_state_->NextHandlerIndex();
|
||||
result = new(zone()) TryCatchStatement(index,
|
||||
try_block,
|
||||
catch_scope,
|
||||
catch_variable,
|
||||
catch_block);
|
||||
} else {
|
||||
ASSERT(finally_block != NULL);
|
||||
result = new(zone()) TryFinallyStatement(try_block, finally_block);
|
||||
int index = current_function_state_->NextHandlerIndex();
|
||||
result = new(zone()) TryFinallyStatement(index,
|
||||
try_block,
|
||||
finally_block);
|
||||
// Combine the jump targets of the try block and the possible catch block.
|
||||
try_collector.targets()->AddAll(*catch_collector.targets());
|
||||
}
|
||||
@ -3897,9 +3910,10 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name,
|
||||
Scope* scope = (type == FunctionLiteral::DECLARATION && !harmony_scoping_)
|
||||
? NewScope(top_scope_->DeclarationScope(), FUNCTION_SCOPE)
|
||||
: NewScope(top_scope_, FUNCTION_SCOPE);
|
||||
ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(8);
|
||||
ZoneList<Statement*>* body = NULL;
|
||||
int materialized_literal_count;
|
||||
int expected_property_count;
|
||||
int handler_count = 0;
|
||||
bool only_simple_this_property_assignments;
|
||||
Handle<FixedArray> this_property_assignments;
|
||||
bool has_duplicate_parameters = false;
|
||||
@ -3955,25 +3969,17 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name,
|
||||
// NOTE: We create a proxy and resolve it here so that in the
|
||||
// future we can change the AST to only refer to VariableProxies
|
||||
// instead of Variables and Proxis as is the case now.
|
||||
Variable* fvar = NULL;
|
||||
Token::Value fvar_init_op = Token::INIT_CONST;
|
||||
if (type == FunctionLiteral::NAMED_EXPRESSION) {
|
||||
VariableMode fvar_mode;
|
||||
Token::Value fvar_init_op;
|
||||
if (harmony_scoping_) {
|
||||
fvar_mode = CONST_HARMONY;
|
||||
fvar_init_op = Token::INIT_CONST_HARMONY;
|
||||
} else {
|
||||
fvar_mode = CONST;
|
||||
fvar_init_op = Token::INIT_CONST;
|
||||
}
|
||||
Variable* fvar = top_scope_->DeclareFunctionVar(function_name, fvar_mode);
|
||||
VariableProxy* fproxy = top_scope_->NewUnresolved(function_name);
|
||||
fproxy->BindTo(fvar);
|
||||
body->Add(new(zone()) ExpressionStatement(
|
||||
new(zone()) Assignment(isolate(),
|
||||
fvar_init_op,
|
||||
fproxy,
|
||||
new(zone()) ThisFunction(isolate()),
|
||||
RelocInfo::kNoPosition)));
|
||||
fvar = top_scope_->DeclareFunctionVar(function_name, fvar_mode);
|
||||
}
|
||||
|
||||
// Determine if the function will be lazily compiled. The mode can only
|
||||
@ -4013,10 +4019,22 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name,
|
||||
}
|
||||
|
||||
if (!is_lazily_compiled) {
|
||||
body = new(zone()) ZoneList<Statement*>(8);
|
||||
if (fvar != NULL) {
|
||||
VariableProxy* fproxy = top_scope_->NewUnresolved(function_name);
|
||||
fproxy->BindTo(fvar);
|
||||
body->Add(new(zone()) ExpressionStatement(
|
||||
new(zone()) Assignment(isolate(),
|
||||
fvar_init_op,
|
||||
fproxy,
|
||||
new(zone()) ThisFunction(isolate()),
|
||||
RelocInfo::kNoPosition)));
|
||||
}
|
||||
ParseSourceElements(body, Token::RBRACE, CHECK_OK);
|
||||
|
||||
materialized_literal_count = function_state.materialized_literal_count();
|
||||
expected_property_count = function_state.expected_property_count();
|
||||
handler_count = function_state.handler_count();
|
||||
only_simple_this_property_assignments =
|
||||
function_state.only_simple_this_property_assignments();
|
||||
this_property_assignments = function_state.this_property_assignments();
|
||||
@ -4084,6 +4102,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name,
|
||||
body,
|
||||
materialized_literal_count,
|
||||
expected_property_count,
|
||||
handler_count,
|
||||
only_simple_this_property_assignments,
|
||||
this_property_assignments,
|
||||
num_parameters,
|
||||
|
12
src/parser.h
12
src/parser.h
@ -77,7 +77,7 @@ class FunctionEntry BASE_EMBEDDED {
|
||||
};
|
||||
|
||||
explicit FunctionEntry(Vector<unsigned> backing) : backing_(backing) { }
|
||||
FunctionEntry() : backing_(Vector<unsigned>::empty()) { }
|
||||
FunctionEntry() { }
|
||||
|
||||
int start_pos() { return backing_[kStartPositionIndex]; }
|
||||
int end_pos() { return backing_[kEndPositionIndex]; }
|
||||
@ -104,7 +104,7 @@ class ScriptDataImpl : public ScriptData {
|
||||
|
||||
// Create an empty ScriptDataImpl that is guaranteed to not satisfy
|
||||
// a SanityCheck.
|
||||
ScriptDataImpl() : store_(Vector<unsigned>()), owns_store_(false) { }
|
||||
ScriptDataImpl() : owns_store_(false) { }
|
||||
|
||||
virtual ~ScriptDataImpl();
|
||||
virtual int Length();
|
||||
@ -734,16 +734,14 @@ class Parser {
|
||||
Scanner scanner_;
|
||||
|
||||
Scope* top_scope_;
|
||||
|
||||
FunctionState* current_function_state_;
|
||||
Mode mode_;
|
||||
|
||||
Target* target_stack_; // for break, continue statements
|
||||
bool allow_natives_syntax_;
|
||||
v8::Extension* extension_;
|
||||
bool is_pre_parsing_;
|
||||
ScriptDataImpl* pre_data_;
|
||||
FuncNameInferrer* fni_;
|
||||
|
||||
Mode mode_;
|
||||
bool allow_natives_syntax_;
|
||||
bool stack_overflow_;
|
||||
// If true, the next (and immediately following) function literal is
|
||||
// preceded by a parenthesis.
|
||||
|
@ -127,7 +127,6 @@ class DescriptorArray;
|
||||
class Expression;
|
||||
class ExternalReference;
|
||||
class FixedArray;
|
||||
class FunctionEntry;
|
||||
class FunctionLiteral;
|
||||
class FunctionTemplateInfo;
|
||||
class MemoryChunk;
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2006-2008 the V8 project authors. All rights reserved.
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
@ -60,6 +60,10 @@ class Memory {
|
||||
return *reinterpret_cast<int*>(addr);
|
||||
}
|
||||
|
||||
static unsigned& unsigned_at(Address addr) {
|
||||
return *reinterpret_cast<unsigned*>(addr);
|
||||
}
|
||||
|
||||
static double& double_at(Address addr) {
|
||||
return *reinterpret_cast<double*>(addr);
|
||||
}
|
||||
|
@ -649,7 +649,6 @@ class Assembler : public AssemblerBase {
|
||||
void push_imm32(int32_t imm32);
|
||||
void push(Register src);
|
||||
void push(const Operand& src);
|
||||
void push(Handle<Object> handle);
|
||||
|
||||
void pop(Register dst);
|
||||
void pop(const Operand& dst);
|
||||
|
@ -3348,7 +3348,7 @@ void StackCheckStub::Generate(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void CallFunctionStub::FinishCode(Code* code) {
|
||||
void CallFunctionStub::FinishCode(Handle<Code> code) {
|
||||
code->set_has_function_cache(false);
|
||||
}
|
||||
|
||||
@ -3704,7 +3704,7 @@ void CEntryStub::Generate(MacroAssembler* masm) {
|
||||
|
||||
|
||||
void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
|
||||
Label invoke, exit;
|
||||
Label invoke, handler_entry, exit;
|
||||
Label not_outermost_js, not_outermost_js_2;
|
||||
{ // NOLINT. Scope block confuses linter.
|
||||
MacroAssembler::NoRootArrayScope uninitialized_root_register(masm);
|
||||
@ -3764,20 +3764,23 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
|
||||
__ Push(Smi::FromInt(StackFrame::INNER_JSENTRY_FRAME));
|
||||
__ bind(&cont);
|
||||
|
||||
// Call a faked try-block that does the invoke.
|
||||
__ call(&invoke);
|
||||
|
||||
// Caught exception: Store result (exception) in the pending
|
||||
// exception field in the JSEnv and return a failure sentinel.
|
||||
// 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.
|
||||
ExternalReference pending_exception(Isolate::kPendingExceptionAddress,
|
||||
isolate);
|
||||
__ Store(pending_exception, rax);
|
||||
__ movq(rax, Failure::Exception(), RelocInfo::NONE);
|
||||
__ jmp(&exit);
|
||||
|
||||
// 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);
|
||||
|
||||
// Clear any pending exceptions.
|
||||
__ LoadRoot(rax, Heap::kTheHoleValueRootIndex);
|
||||
@ -3786,11 +3789,11 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
|
||||
// Fake a receiver (NULL).
|
||||
__ push(Immediate(0)); // receiver
|
||||
|
||||
// Invoke the function by calling through JS entry trampoline
|
||||
// builtin and pop the faked function when we return. We load the address
|
||||
// from an external reference instead of inlining the call target address
|
||||
// directly in the code, because the builtin stubs may not have been
|
||||
// generated yet at the time this code is generated.
|
||||
// Invoke the function by calling through JS entry trampoline builtin and
|
||||
// pop the faked function when we return. We load the address from an
|
||||
// external reference instead of inlining the call target address directly
|
||||
// in the code, because the builtin stubs may not have been generated yet
|
||||
// at the time this code is generated.
|
||||
if (is_construct) {
|
||||
ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline,
|
||||
isolate);
|
||||
|
@ -150,7 +150,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_);
|
||||
}
|
||||
|
@ -51,12 +51,12 @@ static const int kNumSafepointRegisters = 16;
|
||||
class StackHandlerConstants : public AllStatic {
|
||||
public:
|
||||
static const int kNextOffset = 0 * kPointerSize;
|
||||
static const int kContextOffset = 1 * kPointerSize;
|
||||
static const int kFPOffset = 2 * kPointerSize;
|
||||
static const int kStateOffset = 3 * kPointerSize;
|
||||
static const int kPCOffset = 4 * 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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -117,6 +117,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");
|
||||
|
||||
|
@ -2417,86 +2417,105 @@ Operand MacroAssembler::SafepointRegisterSlot(Register reg) {
|
||||
|
||||
|
||||
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::kContextOffset == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kStateOffset == 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);
|
||||
|
||||
// The pc (return address) is already on TOS. This code pushes state,
|
||||
// frame pointer, context, and current handler.
|
||||
// We will build up the handler from the bottom by pushing on the stack.
|
||||
// First compute the state and push the frame pointer and context.
|
||||
unsigned state = StackHandler::OffsetField::encode(handler_index);
|
||||
if (try_location == IN_JAVASCRIPT) {
|
||||
if (type == TRY_CATCH_HANDLER) {
|
||||
push(Immediate(StackHandler::TRY_CATCH));
|
||||
} else {
|
||||
push(Immediate(StackHandler::TRY_FINALLY));
|
||||
}
|
||||
push(rbp);
|
||||
push(rsi);
|
||||
state |= (type == TRY_CATCH_HANDLER)
|
||||
? StackHandler::KindField::encode(StackHandler::TRY_CATCH)
|
||||
: StackHandler::KindField::encode(StackHandler::TRY_FINALLY);
|
||||
} else {
|
||||
ASSERT(try_location == IN_JS_ENTRY);
|
||||
// The frame pointer does not point to a JS frame so we save NULL
|
||||
// for rbp. We expect the code throwing an exception to check rbp
|
||||
// before dereferencing it to restore the context.
|
||||
push(Immediate(StackHandler::ENTRY));
|
||||
// The frame pointer does not point to a JS frame so we save NULL for
|
||||
// rbp. We expect the code throwing an exception to check rbp before
|
||||
// dereferencing it to restore the context.
|
||||
push(Immediate(0)); // NULL frame pointer.
|
||||
Push(Smi::FromInt(0)); // No context.
|
||||
state |= StackHandler::KindField::encode(StackHandler::ENTRY);
|
||||
}
|
||||
// Save the current handler.
|
||||
Operand handler_operand =
|
||||
ExternalOperand(ExternalReference(Isolate::kHandlerAddress, isolate()));
|
||||
push(handler_operand);
|
||||
// Link this handler.
|
||||
movq(handler_operand, rsp);
|
||||
|
||||
// Push the state and the code object.
|
||||
push(Immediate(state));
|
||||
Push(CodeObject());
|
||||
|
||||
// Link the current handler as the next handler.
|
||||
ExternalReference handler_address(Isolate::kHandlerAddress, isolate());
|
||||
push(ExternalOperand(handler_address));
|
||||
// Set this new handler as the current one.
|
||||
movq(ExternalOperand(handler_address), rsp);
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::PopTryHandler() {
|
||||
ASSERT_EQ(0, StackHandlerConstants::kNextOffset);
|
||||
// Unlink this handler.
|
||||
Operand handler_operand =
|
||||
ExternalOperand(ExternalReference(Isolate::kHandlerAddress, isolate()));
|
||||
pop(handler_operand);
|
||||
// Remove the remaining fields.
|
||||
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
|
||||
ExternalReference handler_address(Isolate::kHandlerAddress, isolate());
|
||||
pop(ExternalOperand(handler_address));
|
||||
addq(rsp, Immediate(StackHandlerConstants::kSize - kPointerSize));
|
||||
}
|
||||
|
||||
|
||||
void MacroAssembler::JumpToHandlerEntry() {
|
||||
// Compute the handler entry address and jump to it. The handler table is
|
||||
// a fixed array of (smi-tagged) code offsets.
|
||||
// rax = exception, rdi = code object, rdx = state.
|
||||
movq(rbx, FieldOperand(rdi, Code::kHandlerTableOffset));
|
||||
shr(rdx, Immediate(StackHandler::kKindWidth));
|
||||
movq(rdx, FieldOperand(rbx, rdx, times_8, FixedArray::kHeaderSize));
|
||||
SmiToInteger64(rdx, rdx);
|
||||
lea(rdi, FieldOperand(rdi, rdx, times_1, Code::kHeaderSize));
|
||||
jmp(rdi);
|
||||
}
|
||||
|
||||
|
||||
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::kContextOffset == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kStateOffset == 3 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize);
|
||||
// Keep thrown value in rax.
|
||||
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);
|
||||
|
||||
// The exception is expected in rax.
|
||||
if (!value.is(rax)) {
|
||||
movq(rax, value);
|
||||
}
|
||||
|
||||
// Drop the stack pointer to the top of the top handler.
|
||||
ExternalReference handler_address(Isolate::kHandlerAddress, isolate());
|
||||
Operand handler_operand = ExternalOperand(handler_address);
|
||||
movq(rsp, handler_operand);
|
||||
// get next in chain
|
||||
pop(handler_operand);
|
||||
movq(rsp, ExternalOperand(handler_address));
|
||||
// Restore the next handler.
|
||||
pop(ExternalOperand(handler_address));
|
||||
|
||||
// Remove the code object and state, compute the handler address in rdi.
|
||||
pop(rdi); // Code object.
|
||||
pop(rdx); // Offset and state.
|
||||
|
||||
// Restore the context and frame pointer.
|
||||
pop(rsi); // Context.
|
||||
pop(rbp); // Frame pointer.
|
||||
pop(rdx); // State.
|
||||
|
||||
// If the handler is a JS frame, restore the context to the frame.
|
||||
// (rdx == ENTRY) == (rbp == 0) == (rsi == 0), so we could test any
|
||||
// of them.
|
||||
// (kind == ENTRY) == (rbp == 0) == (rsi == 0), so we could test either
|
||||
// rbp or rsi.
|
||||
Label skip;
|
||||
cmpq(rdx, Immediate(StackHandler::ENTRY));
|
||||
j(equal, &skip, Label::kNear);
|
||||
testq(rsi, rsi);
|
||||
j(zero, &skip, Label::kNear);
|
||||
movq(Operand(rbp, StandardFrameConstants::kContextOffset), rsi);
|
||||
bind(&skip);
|
||||
|
||||
ret(0);
|
||||
JumpToHandlerEntry();
|
||||
}
|
||||
|
||||
|
||||
@ -2504,11 +2523,11 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type,
|
||||
Register value) {
|
||||
// Adjust this code if not the case.
|
||||
STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kContextOffset == 1 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize);
|
||||
STATIC_ASSERT(StackHandlerConstants::kStateOffset == 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);
|
||||
|
||||
// The exception is expected in rax.
|
||||
if (type == OUT_OF_MEMORY) {
|
||||
@ -2538,20 +2557,23 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type,
|
||||
movq(rsp, Operand(rsp, StackHandlerConstants::kNextOffset));
|
||||
|
||||
bind(&check_kind);
|
||||
cmpq(Operand(rsp, StackHandlerConstants::kStateOffset),
|
||||
Immediate(StackHandler::ENTRY));
|
||||
j(not_equal, &fetch_next);
|
||||
STATIC_ASSERT(StackHandler::ENTRY == 0);
|
||||
testl(Operand(rsp, StackHandlerConstants::kStateOffset),
|
||||
Immediate(StackHandler::KindField::kMask));
|
||||
j(not_zero, &fetch_next);
|
||||
|
||||
// Set the top handler address to next handler past the top ENTRY handler.
|
||||
pop(ExternalOperand(handler_address));
|
||||
|
||||
// Clear the context and frame pointer (0 was saved in the handler), and
|
||||
// discard the state.
|
||||
// Remove the code object and state, compute the handler address in rdi.
|
||||
pop(rdi); // Code object.
|
||||
pop(rdx); // Offset and state.
|
||||
|
||||
// Clear the context pointer and frame pointer (0 was saved in the handler).
|
||||
pop(rsi);
|
||||
pop(rbp);
|
||||
pop(rdx); // State.
|
||||
|
||||
ret(0);
|
||||
JumpToHandlerEntry();
|
||||
}
|
||||
|
||||
|
||||
|
@ -933,9 +933,10 @@ class MacroAssembler: public Assembler {
|
||||
// ---------------------------------------------------------------------------
|
||||
// Exception handling
|
||||
|
||||
// Push a new try handler and link into try handler chain. The return
|
||||
// address must be pushed before calling this helper.
|
||||
void PushTryHandler(CodeLocation try_location, HandlerType type);
|
||||
// Push a new try handler and link it into try handler chain.
|
||||
void PushTryHandler(CodeLocation try_location,
|
||||
HandlerType type,
|
||||
int handler_index);
|
||||
|
||||
// Unlink the stack handler on top of the stack from the try handler chain.
|
||||
void PopTryHandler();
|
||||
@ -1326,6 +1327,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.
|
||||
Operand SafepointRegisterSlot(Register reg);
|
||||
static int SafepointRegisterStackIndex(int reg_code) {
|
||||
|
Loading…
Reference in New Issue
Block a user