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:
kmillikin@chromium.org 2011-11-11 12:51:42 +00:00
parent 5834284848
commit 1635117db9
34 changed files with 500 additions and 363 deletions

View File

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

View File

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

View File

@ -103,13 +103,13 @@ 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 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

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

View File

@ -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));
} 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));
state |= (type == TRY_CATCH_HANDLER)
? StackHandler::KindField::encode(StackHandler::TRY_CATCH)
: StackHandler::KindField::encode(StackHandler::TRY_FINALLY);
} 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();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
}
@ -1142,12 +1139,12 @@ void FullCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
//
// The try-finally construct can enter the finally block in three ways:
// 1. By exiting the try-block normally. This removes the try-handler and
// calls the finally block code before continuing.
// calls the finally block code before continuing.
// 2. By exiting the try-block with a function-local control flow transfer
// (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.
__ Call(&finally_entry);
__ push(result_register());
__ CallRuntime(Runtime::kReThrow, 1);
}
// 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);
// Finally block implementation.
__ bind(&finally_entry);
{
// Finally block implementation.
Finally finally_block(this);
EnterFinallyBlock();
EnterFinallyBlock();
{ Finally finally_body(this);
Visit(stmt->finally_block());
ExitFinallyBlock(); // Return to the calling code.
}
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);
}

View File

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

View File

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

View File

@ -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,14 +4773,13 @@ 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,
masm->isolate());
ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline,
masm->isolate());
__ mov(edx, Immediate(construct_entry));
} else {
ExternalReference entry(Builtins::kJSEntryTrampoline,

View File

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

View File

@ -58,13 +58,13 @@ 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 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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,11 +2324,12 @@ 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,
catch_scope,
catch_variable,
catch_block);
int index = current_function_state_->NextHandlerIndex();
TryCatchStatement* statement = new(zone()) TryCatchStatement(index,
try_block,
catch_scope,
catch_variable,
catch_block);
statement->set_escaping_targets(try_collector.targets());
try_block = new(zone()) Block(isolate(), NULL, 1, false);
try_block->AddStatement(statement);
@ -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,
catch_scope,
catch_variable,
catch_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,

View File

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

View File

@ -127,7 +127,6 @@ class DescriptorArray;
class Expression;
class ExternalReference;
class FixedArray;
class FunctionEntry;
class FunctionLiteral;
class FunctionTemplateInfo;
class MemoryChunk;

View File

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

View File

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

View File

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

View File

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

View File

@ -50,13 +50,13 @@ 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 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

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

View File

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

View File

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