From e49d533b509ab712832d8182e7c9eef378036e25 Mon Sep 17 00:00:00 2001 From: "kmillikin@chromium.org" Date: Fri, 11 Nov 2011 13:48:14 +0000 Subject: [PATCH] Reapply "Add a level of indirection to exception handler addresses." Original commit message: 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 BUG= TEST= Review URL: http://codereview.chromium.org/8538011 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9977 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/code-stubs-arm.cc | 25 +++--- src/arm/code-stubs-arm.h | 4 +- src/arm/frames-arm.h | 12 +-- src/arm/full-codegen-arm.cc | 2 + src/arm/macro-assembler-arm.cc | 138 ++++++++++++++++-------------- src/arm/macro-assembler-arm.h | 10 ++- src/ast.h | 24 ++++-- src/code-stubs.cc | 10 ++- src/code-stubs.h | 16 ++-- src/frames-inl.h | 18 ++-- src/frames.h | 11 ++- src/full-codegen.cc | 100 ++++++++++------------ src/full-codegen.h | 3 + src/heap.cc | 10 +-- src/ia32/code-stubs-ia32.cc | 34 ++++---- src/ia32/code-stubs-ia32.h | 4 +- src/ia32/frames-ia32.h | 12 +-- src/ia32/full-codegen-ia32.cc | 2 + src/ia32/macro-assembler-ia32.cc | 128 ++++++++++++++++------------ src/ia32/macro-assembler-ia32.h | 11 ++- src/objects-inl.h | 1 + src/objects-visiting-inl.h | 8 ++ src/objects.h | 6 +- src/parser.cc | 67 +++++++++------ src/parser.h | 12 ++- src/v8globals.h | 1 - src/v8memory.h | 6 +- src/x64/assembler-x64.h | 1 - src/x64/code-stubs-x64.cc | 31 +++---- src/x64/code-stubs-x64.h | 4 +- src/x64/frames-x64.h | 12 +-- src/x64/full-codegen-x64.cc | 2 + src/x64/macro-assembler-x64.cc | 140 ++++++++++++++++++------------- src/x64/macro-assembler-x64.h | 11 ++- 34 files changed, 509 insertions(+), 367 deletions(-) diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index be85fa15b..bd3d2e697 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -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(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->set_has_function_cache(false); } diff --git a/src/arm/code-stubs-arm.h b/src/arm/code-stubs-arm.h index 4e1261b7e..38ed476cc 100644 --- a/src/arm/code-stubs-arm.h +++ b/src/arm/code-stubs-arm.h @@ -136,7 +136,7 @@ class UnaryOpStub: public CodeStub { return UnaryOpIC::ToState(operand_type_); } - virtual void FinishCode(Code* code) { + virtual void FinishCode(Handle 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->set_binary_op_type(operands_type_); code->set_binary_op_result_type(result_type_); } diff --git a/src/arm/frames-arm.h b/src/arm/frames-arm.h index c66ceee93..d0ea4fc4c 100644 --- a/src/arm/frames-arm.h +++ b/src/arm/frames-arm.h @@ -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; }; diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 8a333ee41..8decb1951 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -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"); diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index 0a10b50aa..4fc3b03ab 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -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); - - // The pc (return address) is passed in register lr. + STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); + + // 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(); } diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h index 8f6803eab..56b4bbb89 100644 --- a/src/arm/macro-assembler-arm.h +++ b/src/arm/macro-assembler-arm.h @@ -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); diff --git a/src/ast.h b/src/ast.h index 12c755b6c..71639d5dd 100644 --- a/src/ast.h +++ b/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* targets) { escaping_targets_ = targets; } + int index() const { return index_; } Block* try_block() const { return try_block_; } ZoneList* 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* 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* body, int materialized_literal_count, int expected_property_count, + int handler_count, bool has_only_simple_this_property_assignments, Handle 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_; diff --git a/src/code-stubs.cc b/src/code-stubs.cc index cfbb815f8..ba7df802f 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -119,7 +119,7 @@ Handle CodeStub::GetCode() { Handle 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 dict = @@ -213,6 +213,14 @@ void InstanceofStub::PrintName(StringStream* stream) { } +void JSEntryStub::FinishCode(Handle code) { + Handle 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: diff --git a/src/code-stubs.h b/src/code-stubs.h index 56aa27b6f..83b0f4914 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -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) { } // 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 { }; class StateField: public BitField { }; - virtual void FinishCode(Code* code) { code->set_compare_state(state_); } + virtual void FinishCode(Handle 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->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); + + 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); 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->set_to_boolean_state(types_.ToByte()); } diff --git a/src/frames-inl.h b/src/frames-inl.h index 94c745cfc..af3ae3dfb 100644 --- a/src/frames-inl.h +++ b/src/frames-inl.h @@ -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(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() + offset); +inline Object** StackHandler::code_address() const { + const int offset = StackHandlerConstants::kCodeOffset; + return reinterpret_cast(address() + offset); } diff --git a/src/frames.h b/src/frames.h index 778b80316..2c5e571ed 100644 --- a/src/frames.h +++ b/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 {}; + class OffsetField: public BitField {}; + // 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); }; diff --git a/src/full-codegen.cc b/src/full-codegen.cc index 1589b86a5..794275248 100644 --- a/src/full-codegen.cc +++ b/src/full-codegen.cc @@ -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; - - // 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); - } + Label try_entry, handler_entry, finally_entry; + + // 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); } diff --git a/src/full-codegen.h b/src/full-codegen.h index 799f96d08..59bdf4525 100644 --- a/src/full-codegen.h +++ b/src/full-codegen.h @@ -93,6 +93,8 @@ class FullCodeGenerator: public AstVisitor { void Generate(CompilationInfo* info); void PopulateDeoptimizationData(Handle code); + Handle handler_table() { return handler_table_; } + class StateField : public BitField { }; class PcField : public BitField { }; @@ -755,6 +757,7 @@ class FullCodeGenerator: public AstVisitor { const ExpressionContext* context_; ZoneList bailout_entries_; ZoneList stack_checks_; + Handle handler_table_; friend class NestedStatement; diff --git a/src/heap.cc b/src/heap.cc index f25a79281..bdc8ab31a 100644 --- a/src/heap.cc +++ b/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. diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index 6a96a5b8c..4d69bf8b5 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -4337,7 +4337,7 @@ void StackCheckStub::Generate(MacroAssembler* masm) { } -void CallFunctionStub::FinishCode(Code* code) { +void CallFunctionStub::FinishCode(Handle 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(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, diff --git a/src/ia32/code-stubs-ia32.h b/src/ia32/code-stubs-ia32.h index 707d34622..4d23c3a17 100644 --- a/src/ia32/code-stubs-ia32.h +++ b/src/ia32/code-stubs-ia32.h @@ -147,7 +147,7 @@ class UnaryOpStub: public CodeStub { return UnaryOpIC::ToState(operand_type_); } - virtual void FinishCode(Code* code) { + virtual void FinishCode(Handle 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->set_binary_op_type(operands_type_); code->set_binary_op_result_type(result_type_); } diff --git a/src/ia32/frames-ia32.h b/src/ia32/frames-ia32.h index 2f1b2a96d..32017ea54 100644 --- a/src/ia32/frames-ia32.h +++ b/src/ia32/frames-ia32.h @@ -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; }; diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index bd5df170c..7ad117304 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -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"); diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index bb4f71b38..2d4f4cdb2 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -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(); } diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index 68a14bb3a..6e6bda95a 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -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); diff --git a/src/objects-inl.h b/src/objects-inl.h index 4060969f1..1e5a4da64 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -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) diff --git a/src/objects-visiting-inl.h b/src/objects-visiting-inl.h index 6f0f61d35..12b044ca9 100644 --- a/src/objects-visiting-inl.h +++ b/src/objects-visiting-inl.h @@ -104,7 +104,10 @@ void Code::CodeIterateBody(ObjectVisitor* v) { RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT) | RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY); + // There are two places where we iterate code bodies: here and the + // templated CodeIterateBody (below). They should be kept in sync. IteratePointer(v, kRelocationInfoOffset); + IteratePointer(v, kHandlerTableOffset); IteratePointer(v, kDeoptimizationDataOffset); RelocIterator it(this, mode_mask); @@ -124,9 +127,14 @@ void Code::CodeIterateBody(Heap* heap) { RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT) | RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY); + // There are two places where we iterate code bodies: here and the + // non-templated CodeIterateBody (above). They should be kept in sync. StaticVisitor::VisitPointer( heap, reinterpret_cast(this->address() + kRelocationInfoOffset)); + StaticVisitor::VisitPointer( + heap, + reinterpret_cast(this->address() + kHandlerTableOffset)); StaticVisitor::VisitPointer( heap, reinterpret_cast(this->address() + kDeoptimizationDataOffset)); diff --git a/src/objects.h b/src/objects.h index 1c598c731..b5212314d 100644 --- a/src/objects.h +++ b/src/objects.h @@ -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 = diff --git a/src/parser.cc b/src/parser.cc index 80abc63b4..08395106a 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -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 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