From b4fd72b3354fe2361ef9015c0e2a66fcfdfca678 Mon Sep 17 00:00:00 2001 From: "lrn@chromium.org" Date: Tue, 15 Feb 2011 13:53:51 +0000 Subject: [PATCH] Change native RegExp call code to properly set C++ structures and to handle exceptions on return from RegExp. BUG=1108 TEST= Review URL: http://codereview.chromium.org/6489001 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6794 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/code-stubs-arm.cc | 158 ++++++------------ src/arm/code-stubs-arm.h | 1 + src/arm/macro-assembler-arm.cc | 127 ++++++++++++++- src/arm/macro-assembler-arm.h | 11 +- src/arm/regexp-macro-assembler-arm.cc | 71 ++++---- src/arm/regexp-macro-assembler-arm.h | 3 +- src/arm/simulator-arm.h | 4 +- src/code-stubs.h | 3 - src/ia32/code-stubs-ia32.cc | 107 ++++-------- src/ia32/macro-assembler-ia32.cc | 91 +++++++++++ src/ia32/macro-assembler-ia32.h | 5 + src/macro-assembler.h | 7 + src/x64/code-stubs-x64.cc | 223 ++++++++++---------------- src/x64/macro-assembler-x64.cc | 90 +++++++++++ src/x64/macro-assembler-x64.h | 7 + 15 files changed, 536 insertions(+), 372 deletions(-) diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index 86c29854b..cc49f7e4e 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -3299,105 +3299,13 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) { void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { - // r0 holds the exception. - - // Adjust this code if not the case. - STATIC_ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); - - // Drop the sp to the top of the handler. - __ mov(r3, Operand(ExternalReference(Top::k_handler_address))); - __ ldr(sp, MemOperand(r3)); - - // Restore the next handler and frame pointer, discard handler state. - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); - __ pop(r2); - __ str(r2, MemOperand(r3)); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); - __ ldm(ia_w, sp, r3.bit() | fp.bit()); // r3: discarded state. - - // Before returning we restore the context from the frame pointer if - // not NULL. The frame pointer is NULL in the exception handler of a - // JS entry frame. - __ cmp(fp, Operand(0, RelocInfo::NONE)); - // Set cp to NULL if fp is NULL. - __ mov(cp, Operand(0, RelocInfo::NONE), LeaveCC, eq); - // Restore cp otherwise. - __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne); -#ifdef DEBUG - if (FLAG_debug_code) { - __ mov(lr, Operand(pc)); - } -#endif - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize); - __ pop(pc); + __ Throw(r0); } void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm, UncatchableExceptionType type) { - // Adjust this code if not the case. - STATIC_ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); - - // Drop sp to the top stack handler. - __ mov(r3, Operand(ExternalReference(Top::k_handler_address))); - __ ldr(sp, MemOperand(r3)); - - // Unwind the handlers until the ENTRY handler is found. - Label loop, done; - __ bind(&loop); - // Load the type of the current stack handler. - const int kStateOffset = StackHandlerConstants::kStateOffset; - __ ldr(r2, MemOperand(sp, kStateOffset)); - __ cmp(r2, Operand(StackHandler::ENTRY)); - __ b(eq, &done); - // Fetch the next handler in the list. - const int kNextOffset = StackHandlerConstants::kNextOffset; - __ ldr(sp, MemOperand(sp, kNextOffset)); - __ jmp(&loop); - __ bind(&done); - - // Set the top handler address to next handler past the current ENTRY handler. - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); - __ pop(r2); - __ str(r2, MemOperand(r3)); - - if (type == OUT_OF_MEMORY) { - // Set external caught exception to false. - ExternalReference external_caught(Top::k_external_caught_exception_address); - __ mov(r0, Operand(false, RelocInfo::NONE)); - __ mov(r2, Operand(external_caught)); - __ str(r0, MemOperand(r2)); - - // Set pending exception and r0 to out of memory exception. - Failure* out_of_memory = Failure::OutOfMemoryException(); - __ mov(r0, Operand(reinterpret_cast(out_of_memory))); - __ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address))); - __ str(r0, MemOperand(r2)); - } - - // Stack layout at this point. See also StackHandlerConstants. - // sp -> state (ENTRY) - // fp - // lr - - // Discard handler state (r2 is not used) and restore frame pointer. - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); - __ ldm(ia_w, sp, r2.bit() | fp.bit()); // r2: discarded state. - // Before returning we restore the context from the frame pointer if - // not NULL. The frame pointer is NULL in the exception handler of a - // JS entry frame. - __ cmp(fp, Operand(0, RelocInfo::NONE)); - // Set cp to NULL if fp is NULL. - __ mov(cp, Operand(0, RelocInfo::NONE), LeaveCC, eq); - // Restore cp otherwise. - __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne); -#ifdef DEBUG - if (FLAG_debug_code) { - __ mov(lr, Operand(pc)); - } -#endif - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize); - __ pop(pc); + __ ThrowUncatchable(type, r0); } @@ -3484,7 +3392,9 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, // r0:r1: result // sp: stack pointer // fp: frame pointer - __ LeaveExitFrame(save_doubles_); + // Callee-saved register r4 still holds argc. + __ LeaveExitFrame(save_doubles_, r4); + __ mov(pc, lr); // check if we should retry or throw exception Label retry; @@ -4263,24 +4173,27 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ IncrementCounter(&Counters::regexp_entry_native, 1, r0, r2); static const int kRegExpExecuteArguments = 7; - __ push(lr); - __ PrepareCallCFunction(kRegExpExecuteArguments, r0); + static const int kParameterRegisters = 4; + __ EnterExitFrame(false, kRegExpExecuteArguments - kParameterRegisters); + + // Stack pointer now points to cell where return address is to be written. + // Arguments are before that on the stack or in registers. - // Argument 7 (sp[8]): Indicate that this is a direct call from JavaScript. + // Argument 7 (sp[12]): Indicate that this is a direct call from JavaScript. __ mov(r0, Operand(1)); - __ str(r0, MemOperand(sp, 2 * kPointerSize)); + __ str(r0, MemOperand(sp, 3 * kPointerSize)); - // Argument 6 (sp[4]): Start (high end) of backtracking stack memory area. + // Argument 6 (sp[8]): Start (high end) of backtracking stack memory area. __ mov(r0, Operand(address_of_regexp_stack_memory_address)); __ ldr(r0, MemOperand(r0, 0)); __ mov(r2, Operand(address_of_regexp_stack_memory_size)); __ ldr(r2, MemOperand(r2, 0)); __ add(r0, r0, Operand(r2)); - __ str(r0, MemOperand(sp, 1 * kPointerSize)); + __ str(r0, MemOperand(sp, 2 * kPointerSize)); - // Argument 5 (sp[0]): static offsets vector buffer. + // Argument 5 (sp[4]): static offsets vector buffer. __ mov(r0, Operand(ExternalReference::address_of_static_offsets_vector())); - __ str(r0, MemOperand(sp, 0 * kPointerSize)); + __ str(r0, MemOperand(sp, 1 * kPointerSize)); // For arguments 4 and 3 get string length, calculate start of string data and // calculate the shift of the index (0 for ASCII and 1 for two byte). @@ -4302,8 +4215,10 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // Locate the code entry and call it. __ add(r7, r7, Operand(Code::kHeaderSize - kHeapObjectTag)); - __ CallCFunction(r7, kRegExpExecuteArguments); - __ pop(lr); + DirectCEntryStub stub; + stub.GenerateCall(masm, r7); + + __ LeaveExitFrame(false, no_reg); // r0: result // subject: subject string (callee saved) @@ -4312,6 +4227,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // Check the result. Label success; + __ cmp(r0, Operand(NativeRegExpMacroAssembler::SUCCESS)); __ b(eq, &success); Label failure; @@ -4324,12 +4240,26 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // stack overflow (on the backtrack stack) was detected in RegExp code but // haven't created the exception yet. Handle that in the runtime system. // TODO(592): Rerunning the RegExp to get the stack overflow exception. - __ mov(r0, Operand(ExternalReference::the_hole_value_location())); - __ ldr(r0, MemOperand(r0, 0)); - __ mov(r1, Operand(ExternalReference(Top::k_pending_exception_address))); + __ mov(r1, Operand(ExternalReference::the_hole_value_location())); __ ldr(r1, MemOperand(r1, 0)); + __ mov(r2, Operand(ExternalReference(Top::k_pending_exception_address))); + __ ldr(r0, MemOperand(r2, 0)); __ cmp(r0, r1); __ b(eq, &runtime); + + __ str(r1, MemOperand(r2, 0)); // Clear pending exception. + + // Check if the exception is a termination. If so, throw as uncatchable. + __ LoadRoot(ip, Heap::kTerminationExceptionRootIndex); + __ cmp(r0, ip); + Label termination_exception; + __ b(eq, &termination_exception); + + __ Throw(r0); // Expects thrown value in r0. + + __ bind(&termination_exception); + __ ThrowUncatchable(TERMINATION, r0); // Expects thrown value in r0. + __ bind(&failure); // For failure and exception return null. __ mov(r0, Operand(Factory::null_value())); @@ -5953,14 +5883,24 @@ void DirectCEntryStub::GenerateCall(MacroAssembler* masm, ApiFunction *function) { __ mov(lr, Operand(reinterpret_cast(GetCode().location()), RelocInfo::CODE_TARGET)); - // Push return address (accessible to GC through exit frame pc). __ mov(r2, Operand(ExternalReference(function, ExternalReference::DIRECT_CALL))); + // Push return address (accessible to GC through exit frame pc). __ str(pc, MemOperand(sp, 0)); __ Jump(r2); // Call the api function. } +void DirectCEntryStub::GenerateCall(MacroAssembler* masm, + Register target) { + __ mov(lr, Operand(reinterpret_cast(GetCode().location()), + RelocInfo::CODE_TARGET)); + // Push return address (accessible to GC through exit frame pc). + __ str(pc, MemOperand(sp, 0)); + __ Jump(target); // Call the C++ function. +} + + void GenerateFastPixelArrayLoad(MacroAssembler* masm, Register receiver, Register key, diff --git a/src/arm/code-stubs-arm.h b/src/arm/code-stubs-arm.h index a018d4aef..baaa2f2bd 100644 --- a/src/arm/code-stubs-arm.h +++ b/src/arm/code-stubs-arm.h @@ -581,6 +581,7 @@ class DirectCEntryStub: public CodeStub { DirectCEntryStub() {} void Generate(MacroAssembler* masm); void GenerateCall(MacroAssembler* masm, ApiFunction *function); + void GenerateCall(MacroAssembler* masm, Register target); private: Major MajorKey() { return DirectCEntry; } diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index af0f45871..eb850cd94 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -714,7 +714,8 @@ int MacroAssembler::ActivationFrameAlignment() { } -void MacroAssembler::LeaveExitFrame(bool save_doubles) { +void MacroAssembler::LeaveExitFrame(bool save_doubles, + Register argument_count) { // Optionally restore all double registers. if (save_doubles) { for (int i = 0; i < DwVfpRegister::kNumRegisters; i++) { @@ -736,12 +737,12 @@ void MacroAssembler::LeaveExitFrame(bool save_doubles) { str(r3, MemOperand(ip)); #endif - // Tear down the exit frame, pop the arguments, and return. Callee-saved - // register r4 still holds argc. + // Tear down the exit frame, pop the arguments, and return. mov(sp, Operand(fp)); ldm(ia_w, sp, fp.bit() | lr.bit()); - add(sp, sp, Operand(r4, LSL, kPointerSizeLog2)); - mov(pc, lr); + if (argument_count.is_valid()) { + add(sp, sp, Operand(argument_count, LSL, kPointerSizeLog2)); + } } @@ -1005,6 +1006,117 @@ void MacroAssembler::PopTryHandler() { } +void MacroAssembler::Throw(Register value) { + // r0 is expected to hold the exception. + if (!value.is(r0)) { + mov(r0, value); + } + + // Adjust this code if not the case. + STATIC_ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); + + // Drop the sp to the top of the handler. + mov(r3, Operand(ExternalReference(Top::k_handler_address))); + ldr(sp, MemOperand(r3)); + + // Restore the next handler and frame pointer, discard handler state. + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + pop(r2); + str(r2, MemOperand(r3)); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); + ldm(ia_w, sp, r3.bit() | fp.bit()); // r3: discarded state. + + // Before returning we restore the context from the frame pointer if + // not NULL. The frame pointer is NULL in the exception handler of a + // JS entry frame. + cmp(fp, Operand(0, RelocInfo::NONE)); + // Set cp to NULL if fp is NULL. + mov(cp, Operand(0, RelocInfo::NONE), LeaveCC, eq); + // Restore cp otherwise. + ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne); +#ifdef DEBUG + if (FLAG_debug_code) { + mov(lr, Operand(pc)); + } +#endif + STATIC_ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize); + pop(pc); +} + + +void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, + Register value) { + // Adjust this code if not the case. + STATIC_ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); + + // r0 is expected to hold the exception. + if (!value.is(r0)) { + mov(r0, value); + } + + // Drop sp to the top stack handler. + mov(r3, Operand(ExternalReference(Top::k_handler_address))); + ldr(sp, MemOperand(r3)); + + // Unwind the handlers until the ENTRY handler is found. + Label loop, done; + bind(&loop); + // Load the type of the current stack handler. + const int kStateOffset = StackHandlerConstants::kStateOffset; + ldr(r2, MemOperand(sp, kStateOffset)); + cmp(r2, Operand(StackHandler::ENTRY)); + b(eq, &done); + // Fetch the next handler in the list. + const int kNextOffset = StackHandlerConstants::kNextOffset; + ldr(sp, MemOperand(sp, kNextOffset)); + jmp(&loop); + bind(&done); + + // Set the top handler address to next handler past the current ENTRY handler. + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + pop(r2); + str(r2, MemOperand(r3)); + + if (type == OUT_OF_MEMORY) { + // Set external caught exception to false. + ExternalReference external_caught(Top::k_external_caught_exception_address); + mov(r0, Operand(false, RelocInfo::NONE)); + mov(r2, Operand(external_caught)); + str(r0, MemOperand(r2)); + + // Set pending exception and r0 to out of memory exception. + Failure* out_of_memory = Failure::OutOfMemoryException(); + mov(r0, Operand(reinterpret_cast(out_of_memory))); + mov(r2, Operand(ExternalReference(Top::k_pending_exception_address))); + str(r0, MemOperand(r2)); + } + + // Stack layout at this point. See also StackHandlerConstants. + // sp -> state (ENTRY) + // fp + // lr + + // Discard handler state (r2 is not used) and restore frame pointer. + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 2 * kPointerSize); + ldm(ia_w, sp, r2.bit() | fp.bit()); // r2: discarded state. + // Before returning we restore the context from the frame pointer if + // not NULL. The frame pointer is NULL in the exception handler of a + // JS entry frame. + cmp(fp, Operand(0, RelocInfo::NONE)); + // Set cp to NULL if fp is NULL. + mov(cp, Operand(0, RelocInfo::NONE), LeaveCC, eq); + // Restore cp otherwise. + ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne); +#ifdef DEBUG + if (FLAG_debug_code) { + mov(lr, Operand(pc)); + } +#endif + STATIC_ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize); + pop(pc); +} + + void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, Register scratch, Label* miss) { @@ -1554,9 +1666,10 @@ MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn( cmp(r4, r5); b(ne, &promote_scheduled_exception); - // LeaveExitFrame expects unwind space to be in r4. + // LeaveExitFrame expects unwind space to be in a register. mov(r4, Operand(stack_space)); - LeaveExitFrame(false); + LeaveExitFrame(false, r4); + mov(pc, lr); bind(&promote_scheduled_exception); MaybeObject* result = TryTailCallExternalReference( diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h index 072b13db2..354662da3 100644 --- a/src/arm/macro-assembler-arm.h +++ b/src/arm/macro-assembler-arm.h @@ -297,7 +297,9 @@ class MacroAssembler: public Assembler { void EnterExitFrame(bool save_doubles, int stack_space = 0); // Leave the current exit frame. Expects the return value in r0. - void LeaveExitFrame(bool save_doubles); + // Expect the number of values, pushed prior to the exit frame, to + // remove in a register (or no_reg, if there is nothing to remove). + void LeaveExitFrame(bool save_doubles, Register argument_count); // Get the actual activation frame alignment for target environment. static int ActivationFrameAlignment(); @@ -371,6 +373,13 @@ class MacroAssembler: public Assembler { // Must preserve the result register. void PopTryHandler(); + // Passes thrown value (in r0) to the handler of top of the try handler chain. + void Throw(Register value); + + // Propagates an uncatchable exception to the top of the current JS stack's + // handler chain. + void ThrowUncatchable(UncatchableExceptionType type, Register value); + // --------------------------------------------------------------------------- // Inline caching support diff --git a/src/arm/regexp-macro-assembler-arm.cc b/src/arm/regexp-macro-assembler-arm.cc index 94da04240..1f6ed6712 100644 --- a/src/arm/regexp-macro-assembler-arm.cc +++ b/src/arm/regexp-macro-assembler-arm.cc @@ -57,48 +57,57 @@ namespace internal { * - r13/sp : points to tip of C stack. * * The remaining registers are free for computations. - * * Each call to a public method should retain this convention. + * * The stack will have the following structure: - * - direct_call (if 1, direct call from JavaScript code, if 0 call - * through the runtime system) - * - stack_area_base (High end of the memory area to use as - * backtracking stack) - * - int* capture_array (int[num_saved_registers_], for output). - * --- sp when called --- - * - link address - * - backup of registers r4..r11 - * - end of input (Address of end of string) - * - start of input (Address of first character in string) - * - start index (character index of start) - * --- frame pointer ---- - * - void* input_string (location of a handle containing the string) - * - Offset of location before start of input (effectively character - * position -1). Used to initialize capture registers to a non-position. - * - At start (if 1, we are starting at the start of the - * string, otherwise 0) - * - register 0 (Only positions must be stored in the first - * - register 1 num_saved_registers_ registers) - * - ... - * - register num_registers-1 - * --- sp --- + * - fp[48] direct_call (if 1, direct call from JavaScript code, + * if 0, call through the runtime system). + * - fp[44] stack_area_base (High end of the memory area to use as + * backtracking stack). + * - fp[40] int* capture_array (int[num_saved_registers_], for output). + * - fp[36] secondary link/return address used by native call. + * --- sp when called --- + * - fp[32] return address (lr). + * - fp[28] old frame pointer (r11). + * - fp[0..24] backup of registers r4..r10. + * --- frame pointer ---- + * - fp[-4] end of input (Address of end of string). + * - fp[-8] start of input (Address of first character in string). + * - fp[-12] start index (character index of start). + * - fp[-16] void* input_string (location of a handle containing the string). + * - fp[-20] Offset of location before start of input (effectively character + * position -1). Used to initialize capture registers to a + * non-position. + * - fp[-24] At start (if 1, we are starting at the start of the + * string, otherwise 0) + * - fp[-28] register 0 (Only positions must be stored in the first + * - register 1 num_saved_registers_ registers) + * - ... + * - register num_registers-1 + * --- sp --- * * The first num_saved_registers_ registers are initialized to point to * "character -1" in the string (i.e., char_size() bytes before the first * character of the string). The remaining registers start out as garbage. * * The data up to the return address must be placed there by the calling - * code, by calling the code entry as cast to a function with the signature: + * code and the remaining arguments are passed in registers, e.g. by calling the + * code entry as cast to a function with the signature: * int (*match)(String* input_string, * int start_index, * Address start, * Address end, + * Address secondary_return_address, // Only used by native call. * int* capture_output_array, - * bool at_start, * byte* stack_area_base, - * bool direct_call) + * bool direct_call = false) * The call is performed by NativeRegExpMacroAssembler::Execute() - * (in regexp-macro-assembler.cc). + * (in regexp-macro-assembler.cc) via the CALL_GENERATED_REGEXP_CODE macro + * in arm/simulator-arm.h. + * When calling as a non-direct call (i.e., from C++ code), the return address + * area is overwritten with the LR register by the RegExp code. When doing a + * direct call from generated code, the return address is placed there by + * the calling code, as in a normal exit frame. */ #define __ ACCESS_MASM(masm_) @@ -598,16 +607,17 @@ Handle RegExpMacroAssemblerARM::GetCode(Handle source) { // Entry code: __ bind(&entry_label_); - // Push Link register. // Push arguments // Save callee-save registers. // Start new stack frame. + // Store link register in existing stack-cell. // Order here should correspond to order of offset constants in header file. RegList registers_to_retain = r4.bit() | r5.bit() | r6.bit() | r7.bit() | r8.bit() | r9.bit() | r10.bit() | fp.bit(); RegList argument_registers = r0.bit() | r1.bit() | r2.bit() | r3.bit(); __ stm(db_w, sp, argument_registers | registers_to_retain | lr.bit()); - // Set frame pointer just above the arguments. + // Set frame pointer in space for it if this is not a direct call + // from generated code. __ add(frame_pointer(), sp, Operand(4 * kPointerSize)); __ push(r0); // Make room for "position - 1" constant (value is irrelevant). __ push(r0); // Make room for "at start" constant (value is irrelevant). @@ -764,10 +774,9 @@ Handle RegExpMacroAssemblerARM::GetCode(Handle source) { if (stack_overflow_label_.is_linked()) { SafeCallTarget(&stack_overflow_label_); // Reached if the backtrack-stack limit has been hit. - Label grow_failed; - // Call GrowStack(backtrack_stackpointer()) + // Call GrowStack(backtrack_stackpointer(), &stack_base) static const int num_arguments = 2; __ PrepareCallCFunction(num_arguments, r0); __ mov(r0, backtrack_stackpointer()); diff --git a/src/arm/regexp-macro-assembler-arm.h b/src/arm/regexp-macro-assembler-arm.h index b487ba59d..d9d0b3562 100644 --- a/src/arm/regexp-macro-assembler-arm.h +++ b/src/arm/regexp-macro-assembler-arm.h @@ -122,8 +122,9 @@ class RegExpMacroAssemblerARM: public NativeRegExpMacroAssembler { static const int kStoredRegisters = kFramePointer; // Return address (stored from link register, read into pc on return). static const int kReturnAddress = kStoredRegisters + 8 * kPointerSize; + static const int kSecondaryReturnAddress = kReturnAddress + kPointerSize; // Stack parameters placed by caller. - static const int kRegisterOutput = kReturnAddress + kPointerSize; + static const int kRegisterOutput = kSecondaryReturnAddress + kPointerSize; static const int kStackHighEnd = kRegisterOutput + kPointerSize; static const int kDirectCall = kStackHighEnd + kPointerSize; diff --git a/src/arm/simulator-arm.h b/src/arm/simulator-arm.h index 5256ae35b..09fb19b20 100644 --- a/src/arm/simulator-arm.h +++ b/src/arm/simulator-arm.h @@ -51,7 +51,7 @@ namespace internal { // Call the generated regexp code directly. The entry function pointer should // expect seven int/pointer sized arguments and return an int. #define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6) \ - (entry(p0, p1, p2, p3, p4, p5, p6)) + (entry(p0, p1, p2, p3, NULL, p4, p5, p6)) #define TRY_CATCH_FROM_ADDRESS(try_catch_address) \ (reinterpret_cast(try_catch_address)) @@ -363,7 +363,7 @@ class Simulator { #define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6) \ Simulator::current()->Call( \ - FUNCTION_ADDR(entry), 7, p0, p1, p2, p3, p4, p5, p6) + FUNCTION_ADDR(entry), 8, p0, p1, p2, p3, NULL, p4, p5, p6) #define TRY_CATCH_FROM_ADDRESS(try_catch_address) \ try_catch_address == \ diff --git a/src/code-stubs.h b/src/code-stubs.h index 0d0e37ffa..96ac7335c 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -86,9 +86,6 @@ namespace internal { CODE_STUB_LIST_ALL_PLATFORMS(V) \ CODE_STUB_LIST_ARM(V) -// Types of uncatchable exceptions. -enum UncatchableExceptionType { OUT_OF_MEMORY, TERMINATION }; - // Mode to overwrite BinaryExpression values. enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT }; enum UnaryOverwriteMode { UNARY_OVERWRITE, UNARY_NO_OVERWRITE }; diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index af1840497..6331a6e2e 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -3887,7 +3887,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ IncrementCounter(&Counters::regexp_entry_native, 1); static const int kRegExpExecuteArguments = 7; - __ PrepareCallCFunction(kRegExpExecuteArguments, ecx); + __ EnterApiExitFrame(kRegExpExecuteArguments); // Argument 7: Indicate that this is a direct call from JavaScript. __ mov(Operand(esp, 6 * kPointerSize), Immediate(1)); @@ -3932,7 +3932,10 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // Locate the code entry and call it. __ add(Operand(edx), Immediate(Code::kHeaderSize - kHeapObjectTag)); - __ CallCFunction(edx, kRegExpExecuteArguments); + __ call(Operand(edx)); + + // Drop arguments and come back to JS mode. + __ LeaveApiExitFrame(); // Check the result. Label success; @@ -3949,12 +3952,30 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // haven't created the exception yet. Handle that in the runtime system. // TODO(592): Rerunning the RegExp to get the stack overflow exception. ExternalReference pending_exception(Top::k_pending_exception_address); - __ mov(eax, + __ mov(edx, Operand::StaticVariable(ExternalReference::the_hole_value_location())); - __ cmp(eax, Operand::StaticVariable(pending_exception)); + __ mov(eax, Operand::StaticVariable(pending_exception)); + __ cmp(edx, Operand(eax)); __ j(equal, &runtime); + // For exception, throw the exception again. + + // Clear the pending exception variable. + __ mov(Operand::StaticVariable(pending_exception), edx); + + // Special handling of termination exceptions which are uncatchable + // by javascript code. + __ cmp(eax, Factory::termination_exception()); + Label throw_termination_exception; + __ j(equal, &throw_termination_exception); + + // Handle normal exception by following handler chain. + __ Throw(eax); + + __ bind(&throw_termination_exception); + __ ThrowUncatchable(TERMINATION, eax); + __ bind(&failure); - // For failure and exception return null. + // For failure to match, return null. __ mov(Operand(eax), Factory::null_value()); __ ret(4 * kPointerSize); @@ -4628,34 +4649,7 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { - // eax holds the exception. - - // Adjust this code if not the case. - STATIC_ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); - - // Drop the sp to the top of the handler. - ExternalReference handler_address(Top::k_handler_address); - __ mov(esp, Operand::StaticVariable(handler_address)); - - // Restore next handler and frame pointer, discard handler state. - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); - __ pop(Operand::StaticVariable(handler_address)); - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 1 * kPointerSize); - __ pop(ebp); - __ pop(edx); // Remove state. - - // Before returning we restore the context from the frame pointer if - // not NULL. The frame pointer is NULL in the exception handler of - // a JS entry frame. - __ Set(esi, Immediate(0)); // Tentatively set context pointer to NULL. - NearLabel skip; - __ cmp(ebp, 0); - __ j(equal, &skip, not_taken); - __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); - __ bind(&skip); - - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize); - __ ret(0); + __ Throw(eax); } @@ -4778,52 +4772,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm, UncatchableExceptionType type) { - // Adjust this code if not the case. - STATIC_ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); - - // Drop sp to the top stack handler. - ExternalReference handler_address(Top::k_handler_address); - __ mov(esp, Operand::StaticVariable(handler_address)); - - // Unwind the handlers until the ENTRY handler is found. - NearLabel loop, done; - __ bind(&loop); - // Load the type of the current stack handler. - const int kStateOffset = StackHandlerConstants::kStateOffset; - __ cmp(Operand(esp, kStateOffset), Immediate(StackHandler::ENTRY)); - __ j(equal, &done); - // Fetch the next handler in the list. - const int kNextOffset = StackHandlerConstants::kNextOffset; - __ mov(esp, Operand(esp, kNextOffset)); - __ jmp(&loop); - __ bind(&done); - - // Set the top handler address to next handler past the current ENTRY handler. - STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); - __ pop(Operand::StaticVariable(handler_address)); - - if (type == OUT_OF_MEMORY) { - // Set external caught exception to false. - ExternalReference external_caught(Top::k_external_caught_exception_address); - __ mov(eax, false); - __ mov(Operand::StaticVariable(external_caught), eax); - - // Set pending exception and eax to out of memory exception. - ExternalReference pending_exception(Top::k_pending_exception_address); - __ mov(eax, reinterpret_cast(Failure::OutOfMemoryException())); - __ mov(Operand::StaticVariable(pending_exception), eax); - } - - // Clear the context pointer. - __ Set(esi, Immediate(0)); - - // Restore fp from handler and discard handler state. - STATIC_ASSERT(StackHandlerConstants::kFPOffset == 1 * kPointerSize); - __ pop(ebp); - __ pop(edx); // State. - - STATIC_ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize); - __ ret(0); + __ ThrowUncatchable(type, eax); } diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index cd612b52b..03f726ca9 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -448,6 +448,97 @@ void MacroAssembler::PopTryHandler() { } +void MacroAssembler::Throw(Register value) { + // Adjust this code if not the case. + STATIC_ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); + + // eax must hold the exception. + if (!value.is(eax)) { + mov(eax, value); + } + + // Drop the sp to the top of the handler. + ExternalReference handler_address(Top::k_handler_address); + mov(esp, Operand::StaticVariable(handler_address)); + + // Restore next handler and frame pointer, discard handler state. + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + pop(Operand::StaticVariable(handler_address)); + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 1 * kPointerSize); + pop(ebp); + pop(edx); // Remove state. + + // Before returning we restore the context from the frame pointer if + // not NULL. The frame pointer is NULL in the exception handler of + // a JS entry frame. + Set(esi, Immediate(0)); // Tentatively set context pointer to NULL. + NearLabel skip; + cmp(ebp, 0); + j(equal, &skip, not_taken); + mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + bind(&skip); + + STATIC_ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize); + ret(0); +} + + +void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, + Register value) { + // Adjust this code if not the case. + STATIC_ASSERT(StackHandlerConstants::kSize == 4 * kPointerSize); + + // eax must hold the exception. + if (!value.is(eax)) { + mov(eax, value); + } + + // Drop sp to the top stack handler. + ExternalReference handler_address(Top::k_handler_address); + mov(esp, Operand::StaticVariable(handler_address)); + + // Unwind the handlers until the ENTRY handler is found. + NearLabel loop, done; + bind(&loop); + // Load the type of the current stack handler. + const int kStateOffset = StackHandlerConstants::kStateOffset; + cmp(Operand(esp, kStateOffset), Immediate(StackHandler::ENTRY)); + j(equal, &done); + // Fetch the next handler in the list. + const int kNextOffset = StackHandlerConstants::kNextOffset; + mov(esp, Operand(esp, kNextOffset)); + jmp(&loop); + bind(&done); + + // Set the top handler address to next handler past the current ENTRY handler. + STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); + pop(Operand::StaticVariable(handler_address)); + + if (type == OUT_OF_MEMORY) { + // Set external caught exception to false. + ExternalReference external_caught(Top::k_external_caught_exception_address); + mov(eax, false); + mov(Operand::StaticVariable(external_caught), eax); + + // Set pending exception and eax to out of memory exception. + ExternalReference pending_exception(Top::k_pending_exception_address); + mov(eax, reinterpret_cast(Failure::OutOfMemoryException())); + mov(Operand::StaticVariable(pending_exception), eax); + } + + // Clear the context pointer. + Set(esi, Immediate(0)); + + // Restore fp from handler and discard handler state. + STATIC_ASSERT(StackHandlerConstants::kFPOffset == 1 * kPointerSize); + pop(ebp); + pop(edx); // State. + + STATIC_ASSERT(StackHandlerConstants::kPCOffset == 3 * kPointerSize); + ret(0); +} + + void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, Register scratch, Label* miss) { diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index 09584f7a4..16361ad23 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -304,6 +304,11 @@ class MacroAssembler: public Assembler { // Unlink the stack handler on top of the stack from the try handler chain. void PopTryHandler(); + // Activate the top handler in the try hander chain. + void Throw(Register value); + + void ThrowUncatchable(UncatchableExceptionType type, Register value); + // --------------------------------------------------------------------------- // Inline caching support diff --git a/src/macro-assembler.h b/src/macro-assembler.h index d261f57da..30838bd76 100644 --- a/src/macro-assembler.h +++ b/src/macro-assembler.h @@ -50,6 +50,13 @@ enum HandlerType { }; +// Types of uncatchable exceptions. +enum UncatchableExceptionType { + OUT_OF_MEMORY, + TERMINATION +}; + + // Invalid depth in prototype chain. const int kInvalidProtoDepth = -1; diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index 768e0f421..0cfe665ce 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -2268,46 +2268,46 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // rcx: RegExp data (FixedArray) // rdx: Number of capture registers // Check that the second argument is a string. - __ movq(rax, Operand(rsp, kSubjectOffset)); - __ JumpIfSmi(rax, &runtime); - Condition is_string = masm->IsObjectStringType(rax, rbx, rbx); + __ movq(rdi, Operand(rsp, kSubjectOffset)); + __ JumpIfSmi(rdi, &runtime); + Condition is_string = masm->IsObjectStringType(rdi, rbx, rbx); __ j(NegateCondition(is_string), &runtime); - // rax: Subject string. - // rcx: RegExp data (FixedArray). + // rdi: Subject string. + // rax: RegExp data (FixedArray). // rdx: Number of capture registers. // Check that the third argument is a positive smi less than the string // length. A negative value will be greater (unsigned comparison). __ movq(rbx, Operand(rsp, kPreviousIndexOffset)); __ JumpIfNotSmi(rbx, &runtime); - __ SmiCompare(rbx, FieldOperand(rax, String::kLengthOffset)); + __ SmiCompare(rbx, FieldOperand(rdi, String::kLengthOffset)); __ j(above_equal, &runtime); - // rcx: RegExp data (FixedArray) + // rax: RegExp data (FixedArray) // rdx: Number of capture registers // Check that the fourth object is a JSArray object. - __ movq(rax, Operand(rsp, kLastMatchInfoOffset)); - __ JumpIfSmi(rax, &runtime); - __ CmpObjectType(rax, JS_ARRAY_TYPE, kScratchRegister); + __ movq(rdi, Operand(rsp, kLastMatchInfoOffset)); + __ JumpIfSmi(rdi, &runtime); + __ CmpObjectType(rdi, JS_ARRAY_TYPE, kScratchRegister); __ j(not_equal, &runtime); // Check that the JSArray is in fast case. - __ movq(rbx, FieldOperand(rax, JSArray::kElementsOffset)); - __ movq(rax, FieldOperand(rbx, HeapObject::kMapOffset)); - __ Cmp(rax, Factory::fixed_array_map()); + __ movq(rbx, FieldOperand(rdi, JSArray::kElementsOffset)); + __ movq(rdi, FieldOperand(rbx, HeapObject::kMapOffset)); + __ Cmp(rdi, Factory::fixed_array_map()); __ j(not_equal, &runtime); // Check that the last match info has space for the capture registers and the // additional information. Ensure no overflow in add. STATIC_ASSERT(FixedArray::kMaxLength < kMaxInt - FixedArray::kLengthOffset); - __ SmiToInteger32(rax, FieldOperand(rbx, FixedArray::kLengthOffset)); + __ SmiToInteger32(rdi, FieldOperand(rbx, FixedArray::kLengthOffset)); __ addl(rdx, Immediate(RegExpImpl::kLastMatchOverhead)); - __ cmpl(rdx, rax); + __ cmpl(rdx, rdi); __ j(greater, &runtime); - // rcx: RegExp data (FixedArray) + // rax: RegExp data (FixedArray) // Check the representation and encoding of the subject string. NearLabel seq_ascii_string, seq_two_byte_string, check_code; - __ movq(rax, Operand(rsp, kSubjectOffset)); - __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset)); + __ movq(rdi, Operand(rsp, kSubjectOffset)); + __ movq(rbx, FieldOperand(rdi, HeapObject::kMapOffset)); __ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset)); // First check for flat two byte string. __ andb(rbx, Immediate( @@ -2328,13 +2328,13 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ testb(rbx, Immediate(kIsNotStringMask | kExternalStringTag)); __ j(not_zero, &runtime); // String is a cons string. - __ movq(rdx, FieldOperand(rax, ConsString::kSecondOffset)); + __ movq(rdx, FieldOperand(rdi, ConsString::kSecondOffset)); __ Cmp(rdx, Factory::empty_string()); __ j(not_equal, &runtime); - __ movq(rax, FieldOperand(rax, ConsString::kFirstOffset)); - __ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset)); + __ movq(rdi, FieldOperand(rdi, ConsString::kFirstOffset)); + __ movq(rbx, FieldOperand(rdi, HeapObject::kMapOffset)); // String is a cons string with empty second part. - // rax: first part of cons string. + // rdi: first part of cons string. // rbx: map of first part of cons string. // Is first part a flat two byte string? __ testb(FieldOperand(rbx, Map::kInstanceTypeOffset), @@ -2347,17 +2347,17 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ j(not_zero, &runtime); __ bind(&seq_ascii_string); - // rax: subject string (sequential ascii) - // rcx: RegExp data (FixedArray) - __ movq(r11, FieldOperand(rcx, JSRegExp::kDataAsciiCodeOffset)); - __ Set(rdi, 1); // Type is ascii. + // rdi: subject string (sequential ascii) + // rax: RegExp data (FixedArray) + __ movq(r11, FieldOperand(rax, JSRegExp::kDataAsciiCodeOffset)); + __ Set(rcx, 1); // Type is ascii. __ jmp(&check_code); __ bind(&seq_two_byte_string); - // rax: subject string (flat two-byte) - // rcx: RegExp data (FixedArray) - __ movq(r11, FieldOperand(rcx, JSRegExp::kDataUC16CodeOffset)); - __ Set(rdi, 0); // Type is two byte. + // rdi: subject string (flat two-byte) + // rax: RegExp data (FixedArray) + __ movq(r11, FieldOperand(rax, JSRegExp::kDataUC16CodeOffset)); + __ Set(rcx, 0); // Type is two byte. __ bind(&check_code); // Check that the irregexp code has been generated for the actual string @@ -2366,27 +2366,24 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ CmpObjectType(r11, CODE_TYPE, kScratchRegister); __ j(not_equal, &runtime); - // rax: subject string - // rdi: encoding of subject string (1 if ascii, 0 if two_byte); + // rdi: subject string + // rcx: encoding of subject string (1 if ascii, 0 if two_byte); // r11: code // Load used arguments before starting to push arguments for call to native // RegExp code to avoid handling changing stack height. __ SmiToInteger64(rbx, Operand(rsp, kPreviousIndexOffset)); - // rax: subject string + // rdi: subject string // rbx: previous index - // rdi: encoding of subject string (1 if ascii 0 if two_byte); + // rcx: encoding of subject string (1 if ascii 0 if two_byte); // r11: code // All checks done. Now push arguments for native regexp code. __ IncrementCounter(&Counters::regexp_entry_native, 1); - // rsi is caller save on Windows and used to pass parameter on Linux. - __ push(rsi); - static const int kRegExpExecuteArguments = 7; - __ PrepareCallCFunction(kRegExpExecuteArguments); int argument_slots_on_stack = masm->ArgumentStackSlotsForCFunctionCall(kRegExpExecuteArguments); + __ EnterApiExitFrame(argument_slots_on_stack); // Clobbers rax! // Argument 7: Indicate that this is a direct call from JavaScript. __ movq(Operand(rsp, (argument_slots_on_stack - 1) * kPointerSize), @@ -2423,60 +2420,57 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { #endif // Keep track on aliasing between argX defined above and the registers used. - // rax: subject string + // rdi: subject string // rbx: previous index - // rdi: encoding of subject string (1 if ascii 0 if two_byte); + // rcx: encoding of subject string (1 if ascii 0 if two_byte); // r11: code // Argument 4: End of string data // Argument 3: Start of string data NearLabel setup_two_byte, setup_rest; - __ testb(rdi, rdi); + __ testb(rcx, rcx); // Last use of rcx as encoding of subject string. __ j(zero, &setup_two_byte); - __ SmiToInteger32(rdi, FieldOperand(rax, String::kLengthOffset)); - __ lea(arg4, FieldOperand(rax, rdi, times_1, SeqAsciiString::kHeaderSize)); - __ lea(arg3, FieldOperand(rax, rbx, times_1, SeqAsciiString::kHeaderSize)); + __ SmiToInteger32(rcx, FieldOperand(rdi, String::kLengthOffset)); + __ lea(arg4, FieldOperand(rdi, rcx, times_1, SeqAsciiString::kHeaderSize)); + __ lea(arg3, FieldOperand(rdi, rbx, times_1, SeqAsciiString::kHeaderSize)); __ jmp(&setup_rest); __ bind(&setup_two_byte); - __ SmiToInteger32(rdi, FieldOperand(rax, String::kLengthOffset)); - __ lea(arg4, FieldOperand(rax, rdi, times_2, SeqTwoByteString::kHeaderSize)); - __ lea(arg3, FieldOperand(rax, rbx, times_2, SeqTwoByteString::kHeaderSize)); + __ SmiToInteger32(rcx, FieldOperand(rdi, String::kLengthOffset)); + __ lea(arg4, FieldOperand(rdi, rcx, times_2, SeqTwoByteString::kHeaderSize)); + __ lea(arg3, FieldOperand(rdi, rbx, times_2, SeqTwoByteString::kHeaderSize)); __ bind(&setup_rest); // Argument 2: Previous index. __ movq(arg2, rbx); // Argument 1: Subject string. - __ movq(arg1, rax); +#ifdef WIN64_ + __ movq(arg1, rdi); +#else + // Already there in AMD64 calling convention. + ASSERT(arg1.is(rdi)); +#endif // Locate the code entry and call it. __ addq(r11, Immediate(Code::kHeaderSize - kHeapObjectTag)); - __ CallCFunction(r11, kRegExpExecuteArguments); + __ call(r11); - // rsi is caller save, as it is used to pass parameter. - __ pop(rsi); + __ LeaveApiExitFrame(); // Check the result. NearLabel success; + Label exception; __ cmpl(rax, Immediate(NativeRegExpMacroAssembler::SUCCESS)); __ j(equal, &success); - NearLabel failure; - __ cmpl(rax, Immediate(NativeRegExpMacroAssembler::FAILURE)); - __ j(equal, &failure); __ cmpl(rax, Immediate(NativeRegExpMacroAssembler::EXCEPTION)); - // If not exception it can only be retry. Handle that in the runtime system. + __ j(equal, &exception); + __ cmpl(rax, Immediate(NativeRegExpMacroAssembler::FAILURE)); + // If none of the above, it can only be retry. + // Handle that in the runtime system. __ j(not_equal, &runtime); - // Result must now be exception. If there is no pending exception already a - // stack overflow (on the backtrack stack) was detected in RegExp code but - // haven't created the exception yet. Handle that in the runtime system. - // TODO(592): Rerunning the RegExp to get the stack overflow exception. - ExternalReference pending_exception_address(Top::k_pending_exception_address); - __ movq(kScratchRegister, pending_exception_address); - __ Cmp(kScratchRegister, Factory::the_hole_value()); - __ j(equal, &runtime); - __ bind(&failure); - // For failure and exception return null. - __ Move(rax, Factory::null_value()); + + // For failure return null. + __ LoadRoot(rax, Heap::kNullValueRootIndex); __ ret(4 * kPointerSize); // Load RegExp data. @@ -2537,6 +2531,27 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ movq(rax, Operand(rsp, kLastMatchInfoOffset)); __ ret(4 * kPointerSize); + __ bind(&exception); + // Result must now be exception. If there is no pending exception already a + // stack overflow (on the backtrack stack) was detected in RegExp code but + // haven't created the exception yet. Handle that in the runtime system. + // TODO(592): Rerunning the RegExp to get the stack overflow exception. + ExternalReference pending_exception_address(Top::k_pending_exception_address); + __ movq(rbx, pending_exception_address); + __ movq(rax, Operand(rbx, 0)); + __ LoadRoot(rdx, Heap::kTheHoleValueRootIndex); + __ cmpq(rax, rdx); + __ j(equal, &runtime); + __ movq(Operand(rbx, 0), rdx); + + __ CompareRoot(rax, Heap::kTerminationExceptionRootIndex); + NearLabel termination_exception; + __ j(equal, &termination_exception); + __ Throw(rax); + + __ bind(&termination_exception); + __ ThrowUncatchable(TERMINATION, rax); + // Do the runtime call to execute the regexp. __ bind(&runtime); __ TailCallRuntime(Runtime::kRegExpExec, 4, 1); @@ -3085,31 +3100,8 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { - // Check that stack should contain next handler, frame pointer, state and - // return address in that order. - STATIC_ASSERT(StackHandlerConstants::kFPOffset + kPointerSize == - StackHandlerConstants::kStateOffset); - STATIC_ASSERT(StackHandlerConstants::kStateOffset + kPointerSize == - StackHandlerConstants::kPCOffset); - - ExternalReference handler_address(Top::k_handler_address); - __ movq(kScratchRegister, handler_address); - __ movq(rsp, Operand(kScratchRegister, 0)); - // get next in chain - __ pop(rcx); - __ movq(Operand(kScratchRegister, 0), rcx); - __ pop(rbp); // pop frame pointer - __ pop(rdx); // remove state - - // Before returning we restore the context from the frame pointer if not NULL. - // The frame pointer is NULL in the exception handler of a JS entry frame. - __ Set(rsi, 0); // Tentatively set context pointer to NULL - NearLabel skip; - __ cmpq(rbp, Immediate(0)); - __ j(equal, &skip); - __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); - __ bind(&skip); - __ ret(0); + // Throw exception in eax. + __ Throw(rax); } @@ -3251,54 +3243,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, void CEntryStub::GenerateThrowUncatchable(MacroAssembler* masm, UncatchableExceptionType type) { - // Fetch top stack handler. - ExternalReference handler_address(Top::k_handler_address); - __ movq(kScratchRegister, handler_address); - __ movq(rsp, Operand(kScratchRegister, 0)); - - // Unwind the handlers until the ENTRY handler is found. - NearLabel loop, done; - __ bind(&loop); - // Load the type of the current stack handler. - const int kStateOffset = StackHandlerConstants::kStateOffset; - __ cmpq(Operand(rsp, kStateOffset), Immediate(StackHandler::ENTRY)); - __ j(equal, &done); - // Fetch the next handler in the list. - const int kNextOffset = StackHandlerConstants::kNextOffset; - __ movq(rsp, Operand(rsp, kNextOffset)); - __ jmp(&loop); - __ bind(&done); - - // Set the top handler address to next handler past the current ENTRY handler. - __ movq(kScratchRegister, handler_address); - __ pop(Operand(kScratchRegister, 0)); - - if (type == OUT_OF_MEMORY) { - // Set external caught exception to false. - ExternalReference external_caught(Top::k_external_caught_exception_address); - __ movq(rax, Immediate(false)); - __ store_rax(external_caught); - - // Set pending exception and rax to out of memory exception. - ExternalReference pending_exception(Top::k_pending_exception_address); - __ movq(rax, Failure::OutOfMemoryException(), RelocInfo::NONE); - __ store_rax(pending_exception); - } - - // Clear the context pointer. - __ Set(rsi, 0); - - // Restore registers from handler. - STATIC_ASSERT(StackHandlerConstants::kNextOffset + kPointerSize == - StackHandlerConstants::kFPOffset); - __ pop(rbp); // FP - STATIC_ASSERT(StackHandlerConstants::kFPOffset + kPointerSize == - StackHandlerConstants::kStateOffset); - __ pop(rdx); // State - - STATIC_ASSERT(StackHandlerConstants::kStateOffset + kPointerSize == - StackHandlerConstants::kPCOffset); - __ ret(0); + __ ThrowUncatchable(type, rax); } diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index e990cfc35..e7d02d200 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -1542,6 +1542,96 @@ void MacroAssembler::PopTryHandler() { } +void MacroAssembler::Throw(Register value) { + // Check that stack should contain next handler, frame pointer, state and + // return address in that order. + STATIC_ASSERT(StackHandlerConstants::kFPOffset + kPointerSize == + StackHandlerConstants::kStateOffset); + STATIC_ASSERT(StackHandlerConstants::kStateOffset + kPointerSize == + StackHandlerConstants::kPCOffset); + // Keep thrown value in rax. + if (!value.is(rax)) { + movq(rax, value); + } + + ExternalReference handler_address(Top::k_handler_address); + movq(kScratchRegister, handler_address); + movq(rsp, Operand(kScratchRegister, 0)); + // get next in chain + pop(rcx); + movq(Operand(kScratchRegister, 0), rcx); + pop(rbp); // pop frame pointer + pop(rdx); // remove state + + // Before returning we restore the context from the frame pointer if not NULL. + // The frame pointer is NULL in the exception handler of a JS entry frame. + Set(rsi, 0); // Tentatively set context pointer to NULL + NearLabel skip; + cmpq(rbp, Immediate(0)); + j(equal, &skip); + movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); + bind(&skip); + ret(0); +} + + +void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, + Register value) { + // Keep thrown value in rax. + if (!value.is(rax)) { + movq(rax, value); + } + // Fetch top stack handler. + ExternalReference handler_address(Top::k_handler_address); + movq(kScratchRegister, handler_address); + movq(rsp, Operand(kScratchRegister, 0)); + + // Unwind the handlers until the ENTRY handler is found. + NearLabel loop, done; + bind(&loop); + // Load the type of the current stack handler. + const int kStateOffset = StackHandlerConstants::kStateOffset; + cmpq(Operand(rsp, kStateOffset), Immediate(StackHandler::ENTRY)); + j(equal, &done); + // Fetch the next handler in the list. + const int kNextOffset = StackHandlerConstants::kNextOffset; + movq(rsp, Operand(rsp, kNextOffset)); + jmp(&loop); + bind(&done); + + // Set the top handler address to next handler past the current ENTRY handler. + movq(kScratchRegister, handler_address); + pop(Operand(kScratchRegister, 0)); + + if (type == OUT_OF_MEMORY) { + // Set external caught exception to false. + ExternalReference external_caught(Top::k_external_caught_exception_address); + movq(rax, Immediate(false)); + store_rax(external_caught); + + // Set pending exception and rax to out of memory exception. + ExternalReference pending_exception(Top::k_pending_exception_address); + movq(rax, Failure::OutOfMemoryException(), RelocInfo::NONE); + store_rax(pending_exception); + } + + // Clear the context pointer. + Set(rsi, 0); + + // Restore registers from handler. + STATIC_ASSERT(StackHandlerConstants::kNextOffset + kPointerSize == + StackHandlerConstants::kFPOffset); + pop(rbp); // FP + STATIC_ASSERT(StackHandlerConstants::kFPOffset + kPointerSize == + StackHandlerConstants::kStateOffset); + pop(rdx); // State + + STATIC_ASSERT(StackHandlerConstants::kStateOffset + kPointerSize == + StackHandlerConstants::kPCOffset); + ret(0); +} + + void MacroAssembler::Ret() { ret(0); } diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h index 8254f2fac..835251832 100644 --- a/src/x64/macro-assembler-x64.h +++ b/src/x64/macro-assembler-x64.h @@ -680,6 +680,13 @@ class MacroAssembler: public Assembler { // Unlink the stack handler on top of the stack from the try handler chain. void PopTryHandler(); + // Activate the top handler in the try hander chain and pass the + // thrown value. + void Throw(Register value); + + // Propagate an uncatchable exception out of the current JS stack. + void ThrowUncatchable(UncatchableExceptionType type, Register value); + // --------------------------------------------------------------------------- // Inline caching support -- 2.34.1