From: lrn@chromium.org Date: Mon, 12 Jan 2009 13:05:23 +0000 (+0000) Subject: Separately growing stack for irregexp ia32 backtrack stack. X-Git-Tag: upstream/4.7.83~24814 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=21d2865757d58691d83e47e0e3f53f56873e8932;p=platform%2Fupstream%2Fv8.git Separately growing stack for irregexp ia32 backtrack stack. git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1053 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/SConscript b/src/SConscript index c1bd12b46..092fe8dae 100644 --- a/src/SConscript +++ b/src/SConscript @@ -44,11 +44,11 @@ SOURCES = { 'interpreter-irregexp.cc', 'jsregexp.cc', 'log.cc', 'mark-compact.cc', 'messages.cc', 'objects.cc', 'parser.cc', 'property.cc', 'regexp-macro-assembler.cc', 'regexp-macro-assembler-irregexp.cc', - 'rewriter.cc', 'runtime.cc', 'scanner.cc', 'scopeinfo.cc', 'scopes.cc', - 'serialize.cc', 'snapshot-common.cc', 'spaces.cc', 'string-stream.cc', - 'stub-cache.cc', 'token.cc', 'top.cc', 'unicode.cc', 'usage-analyzer.cc', - 'utils.cc', 'v8-counters.cc', 'v8.cc', 'v8threads.cc', 'variables.cc', - 'zone.cc' + 'regexp-stack.cc', 'rewriter.cc', 'runtime.cc', 'scanner.cc', + 'scopeinfo.cc', 'scopes.cc', 'serialize.cc', 'snapshot-common.cc', + 'spaces.cc', 'string-stream.cc', 'stub-cache.cc', 'token.cc', 'top.cc', + 'unicode.cc', 'usage-analyzer.cc', 'utils.cc', 'v8-counters.cc', 'v8.cc', + 'v8threads.cc', 'variables.cc', 'zone.cc' ], 'arch:arm': ['assembler-arm.cc', 'builtins-arm.cc', 'codegen-arm.cc', 'cpu-arm.cc', 'debug-arm.cc', 'disasm-arm.cc', 'frames-arm.cc', diff --git a/src/assembler.cc b/src/assembler.cc index c56eb2f73..ad4b24cc1 100644 --- a/src/assembler.cc +++ b/src/assembler.cc @@ -41,6 +41,7 @@ #include "runtime.h" #include "serialize.h" #include "stub-cache.h" +#include "regexp-stack.h" namespace v8 { namespace internal { @@ -551,6 +552,11 @@ ExternalReference ExternalReference::address_of_stack_guard_limit() { } +ExternalReference ExternalReference::address_of_regexp_stack_limit() { + return ExternalReference(RegExpStack::limit_address()); +} + + ExternalReference ExternalReference::debug_break() { return ExternalReference(FUNCTION_ADDR(Debug::Break)); } diff --git a/src/assembler.h b/src/assembler.h index 7bd117cdc..097bc84d2 100644 --- a/src/assembler.h +++ b/src/assembler.h @@ -442,6 +442,9 @@ class ExternalReference BASE_EMBEDDED { // Static variable StackGuard::address_of_limit() static ExternalReference address_of_stack_guard_limit(); + // Static variable RegExpStack::limit_address() + static ExternalReference address_of_regexp_stack_limit(); + // Function Debug::Break() static ExternalReference debug_break(); diff --git a/src/jsregexp.cc b/src/jsregexp.cc index e072d0de4..37ba35f73 100644 --- a/src/jsregexp.cc +++ b/src/jsregexp.cc @@ -40,6 +40,7 @@ #include "regexp-macro-assembler.h" #include "regexp-macro-assembler-tracer.h" #include "regexp-macro-assembler-irregexp.h" +#include "regexp-stack.h" #ifdef ARM #include "regexp-macro-assembler-arm.h" @@ -913,7 +914,7 @@ Handle RegExpImpl::IrregexpExecOnce(Handle irregexp, } res = RegExpMacroAssemblerIA32::Execute( *code, - &address, + const_cast(&address), start_offset << char_size_shift, end_offset << char_size_shift, offsets_vector, @@ -925,7 +926,7 @@ Handle RegExpImpl::IrregexpExecOnce(Handle irregexp, int byte_offset = char_address - reinterpret_cast
(*subject); res = RegExpMacroAssemblerIA32::Execute( *code, - subject.location(), + reinterpret_cast(subject.location()), byte_offset + (start_offset << char_size_shift), byte_offset + (end_offset << char_size_shift), offsets_vector, @@ -1347,8 +1348,18 @@ int GenerationVariant::FindAffectedRegisters(OutSet* affected_registers) { void GenerationVariant::PushAffectedRegisters(RegExpMacroAssembler* assembler, int max_register, OutSet& affected_registers) { - for (int reg = 0; reg <= max_register; reg++) { - if (affected_registers.Get(reg)) assembler->PushRegister(reg); + // Stay safe and check every half times the limit. + // (Round up in case the limit is 1). + int push_limit = (assembler->stack_limit_slack() + 1) / 2; + for (int reg = 0, pushes = 0; reg <= max_register; reg++) { + if (affected_registers.Get(reg)) { + pushes++; + RegExpMacroAssembler::StackCheckFlag check_stack_limit = + (pushes % push_limit) == 0 ? + RegExpMacroAssembler::kCheckStackLimit : + RegExpMacroAssembler::kNoStackLimitCheck; + assembler->PushRegister(reg, check_stack_limit); + } } } diff --git a/src/regexp-macro-assembler-ia32.cc b/src/regexp-macro-assembler-ia32.cc index d743268b9..56846af3d 100644 --- a/src/regexp-macro-assembler-ia32.cc +++ b/src/regexp-macro-assembler-ia32.cc @@ -30,6 +30,7 @@ #include "unicode.h" #include "log.h" #include "ast.h" +#include "regexp-stack.h" #include "macro-assembler.h" #include "regexp-macro-assembler.h" #include "macro-assembler-ia32.h" @@ -46,12 +47,15 @@ namespace v8 { namespace internal { * - esi : end of input (points to byte after last character in input). * - ebp : points to the location above the registers on the stack, * as if by the "enter " opcode. - * - esp : points to tip of backtracking stack. + * - esp : points to tip of C stack. + * - ecx : points to tip of backtrack stack * * The registers eax, ebx and ecx are free to use for computations. * * Each call to a public method should retain this convention. * The stack will have the following structure: + * - stack_area_top (High end of the memory area to use as + * backtracking stack) * - at_start (if 1, start at start of string, if 0, don't) * - int* capture_array (int[num_saved_registers_], for output). * - end of input (index of end of string, relative to *string_base) @@ -59,9 +63,9 @@ namespace v8 { namespace internal { * to *string_base) * - void** string_base (location of a handle containing the string) * - return address - * - backup of esi - * - backup of edi - * - backup of ebx + * - backup of caller esi + * - backup of caller edi + * - backup of caller ebx * ebp-> - old ebp * - register 0 ebp[-4] (Only positions must be stored in the first * - register 1 ebp[-8] num_saved_registers_ registers) @@ -77,7 +81,8 @@ namespace v8 { namespace internal { * int start_offset, * int end_offset, * int* capture_output_array, - * bool at_start) + * bool at_start, + * byte* stack_area_top) */ #define __ masm_-> @@ -110,6 +115,12 @@ RegExpMacroAssemblerIA32::~RegExpMacroAssemblerIA32() { backtrack_label_.Unuse(); exit_label_.Unuse(); check_preempt_label_.Unuse(); + stack_overflow_label_.Unuse(); +} + + +int RegExpMacroAssemblerIA32::stack_limit_slack() { + return RegExpStack::kStackLimitSlack; } @@ -124,12 +135,18 @@ void RegExpMacroAssemblerIA32::AdvanceCurrentPosition(int by) { void RegExpMacroAssemblerIA32::AdvanceRegister(int reg, int by) { ASSERT(reg >= 0); ASSERT(reg < num_registers_); - __ add(register_location(reg), Immediate(by)); + if (by != 0) { + __ add(register_location(reg), Immediate(by)); + } } void RegExpMacroAssemblerIA32::Backtrack() { - SafeReturn(); + CheckPreemption(); + // Pop Code* offset from backtrack stack, add Code* and jump to location. + Pop(ebx); + __ add(Operand(ebx), Immediate(self_)); + __ jmp(Operand(ebx)); } @@ -137,22 +154,11 @@ void RegExpMacroAssemblerIA32::Bind(Label* label) { __ bind(label); } + void RegExpMacroAssemblerIA32::CheckBitmap(uc16 start, Label* bitmap, Label* on_zero) { UNIMPLEMENTED(); - __ mov(eax, current_character()); - __ sub(Operand(eax), Immediate(start)); - __ cmp(eax, 64); // FIXME: 64 = length_of_bitmap_in_bits. - BranchOrBacktrack(greater_equal, on_zero); - __ mov(ebx, eax); - __ shr(ebx, 3); - // TODO(lrn): Where is the bitmap stored? Pass the bitmap as argument instead. - // __ mov(ecx, position_of_bitmap); - __ movzx_b(ebx, Operand(ecx, ebx, times_1, 0)); - __ and_(eax, (1<<3)-1); - __ bt(Operand(ebx), eax); - BranchOrBacktrack(carry, on_zero); } @@ -169,8 +175,10 @@ void RegExpMacroAssemblerIA32::CheckCharacterGT(uc16 limit, Label* on_greater) { void RegExpMacroAssemblerIA32::CheckNotAtStart(Label* on_not_at_start) { + // Did we start the match at the start of the string at all? __ cmp(Operand(ebp, kAtStart), Immediate(0)); BranchOrBacktrack(equal, on_not_at_start); + // If we did, are we still at the start of the input? __ mov(eax, Operand(ebp, kInputEndOffset)); __ add(eax, Operand(edi)); __ cmp(eax, Operand(ebp, kInputStartOffset)); @@ -191,6 +199,7 @@ void RegExpMacroAssemblerIA32::CheckCharacters(Vector str, int byte_length = str.length() * char_size(); int byte_offset = cp_offset * char_size(); if (check_end_of_string) { + // Check that there are at least str.length() characters left in the input. __ cmp(Operand(edi), Immediate(-(byte_offset + byte_length))); BranchOrBacktrack(greater, on_failure); } @@ -222,9 +231,9 @@ void RegExpMacroAssemblerIA32::CheckCharacters(Vector str, void RegExpMacroAssemblerIA32::CheckGreedyLoop(Label* on_equal) { Label fallthrough; - __ cmp(edi, Operand(esp, 0)); + __ cmp(edi, Operand(backtrack_stackpointer(), 0)); __ j(not_equal, &fallthrough); - __ add(Operand(esp), Immediate(4)); // Pop. + __ add(Operand(backtrack_stackpointer()), Immediate(kPointerSize)); // Pop. BranchOrBacktrack(no_condition, on_equal); __ bind(&fallthrough); } @@ -234,20 +243,31 @@ void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase( int start_reg, Label* on_no_match) { Label fallthrough; - __ mov(edx, register_location(start_reg)); - __ mov(ecx, register_location(start_reg + 1)); - __ sub(ecx, Operand(edx)); // Length to check. - BranchOrBacktrack(less, on_no_match); + __ mov(edx, register_location(start_reg)); // Index of start of capture + __ mov(ebx, register_location(start_reg + 1)); // Index of end of capture + __ sub(ebx, Operand(edx)); // Length of capture. + + // The length of a capture should not be negative. This can only happen + // if the end of the capture is unrecorded, or at a point earlier than + // the start of the capture. + BranchOrBacktrack(less, on_no_match, not_taken); + + // If length is zero, either the capture is empty or it is completely + // uncaptured. In either case succeed immediately. __ j(equal, &fallthrough); if (mode_ == ASCII) { Label success; Label fail; Label loop_increment; + // Save register contents to make the registers available below. __ push(edi); - __ add(edx, Operand(esi)); - __ add(edi, Operand(esi)); - __ add(ecx, Operand(edi)); + __ push(backtrack_stackpointer()); + // After this, the eax, ebx, ecx, edx and edi registers are available. + + __ add(edx, Operand(esi)); // Start of capture + __ add(edi, Operand(esi)); // Start of text to match against capture. + __ add(ebx, Operand(edi)); // End of text to match against capture. Label loop; __ bind(&loop); @@ -255,55 +275,71 @@ void RegExpMacroAssemblerIA32::CheckNotBackReferenceIgnoreCase( __ cmpb_al(Operand(edx, 0)); __ j(equal, &loop_increment); - // Compare lower-case if letters. - __ or_(eax, 0x20); // To lower-case. - __ lea(ebx, Operand(eax, -'a')); - __ cmp(ebx, static_cast('z' - 'a')); + // Mismatch, try case-insensitive match (converting letters to lower-case). + __ or_(eax, 0x20); // Convert match character to lower-case. + __ lea(ecx, Operand(eax, -'a')); + __ cmp(ecx, static_cast('z' - 'a')); // Is eax a lowercase letter? __ j(above, &fail); - __ movzx_b(ebx, Operand(edx, 0)); - __ or_(ebx, 0x20); // To-lower-case - __ cmp(eax, Operand(ebx)); + // Also convert capture character. + __ movzx_b(ecx, Operand(edx, 0)); + __ or_(ecx, 0x20); + + __ cmp(eax, Operand(ecx)); __ j(not_equal, &fail); __ bind(&loop_increment); + // Increment pointers into match and capture strings. __ add(Operand(edx), Immediate(1)); __ add(Operand(edi), Immediate(1)); - __ cmp(edi, Operand(ecx)); + // Compare to end of match, and loop if not done. + __ cmp(edi, Operand(ebx)); __ j(below, &loop, taken); __ jmp(&success); __ bind(&fail); + // Restore original values before failing. + __ pop(backtrack_stackpointer()); __ pop(edi); BranchOrBacktrack(no_condition, on_no_match); __ bind(&success); - __ pop(eax); // discard original value of edi + // Restore original value before continuing. + __ pop(backtrack_stackpointer()); + // Drop original value of character position. + __ add(Operand(esp), Immediate(kPointerSize)); + // Compute new value of character position after the matched part. __ sub(edi, Operand(esi)); } else { ASSERT(mode_ == UC16); + // Save registers before calling C function. __ push(esi); __ push(edi); - __ push(ecx); + __ push(backtrack_stackpointer()); + __ push(ebx); const int four_arguments = 4; FrameAlign(four_arguments); - // Put arguments on stack. - __ mov(Operand(esp, 3 * kPointerSize), ecx); - __ mov(ebx, Operand(ebp, kInputEndOffset)); - __ add(edi, Operand(ebx)); + // Put arguments into allocated stack area. + __ mov(Operand(esp, 3 * kPointerSize), ebx); + __ mov(ecx, Operand(ebp, kInputEndOffset)); + __ add(edi, Operand(ecx)); __ mov(Operand(esp, 2 * kPointerSize), edi); - __ add(eax, Operand(ebx)); + __ add(eax, Operand(ecx)); __ mov(Operand(esp, 1 * kPointerSize), eax); __ mov(eax, Operand(ebp, kInputBuffer)); __ mov(Operand(esp, 0 * kPointerSize), eax); Address function_address = FUNCTION_ADDR(&CaseInsensitiveCompareUC16); CallCFunction(function_address, four_arguments); - __ pop(ecx); + // Pop original values before reacting on result value. + __ pop(ebx); + __ pop(backtrack_stackpointer()); __ pop(edi); __ pop(esi); + // Check if function returned non-zero for success or zero for failure. __ or_(eax, Operand(eax)); BranchOrBacktrack(zero, on_no_match); - __ add(edi, Operand(ecx)); + // On success, increment position by length of capture. + __ add(edi, Operand(ebx)); } __ bind(&fallthrough); } @@ -315,45 +351,60 @@ void RegExpMacroAssemblerIA32::CheckNotBackReference( Label fallthrough; Label success; Label fail; + + // Find length of back-referenced capture. __ mov(edx, register_location(start_reg)); - __ mov(ecx, register_location(start_reg + 1)); - __ sub(ecx, Operand(edx)); // Length to check. + __ mov(eax, register_location(start_reg + 1)); + __ sub(eax, Operand(edx)); // Length to check. + // Fail on partial or illegal capture (start of capture after end of capture). BranchOrBacktrack(less, on_no_match); + // Succeed on empty capture (including no capture) __ j(equal, &fallthrough); - // Check that there are sufficient characters left in the input. + // Check that there are sufficient characters left in the input. __ mov(ebx, edi); - __ add(ebx, Operand(ecx)); + __ add(ebx, Operand(eax)); BranchOrBacktrack(greater, on_no_match); - __ mov(ebx, edi); - __ add(edi, Operand(esi)); - __ add(edx, Operand(esi)); - __ add(ecx, Operand(edi)); + // Save register to make it available below. + __ push(backtrack_stackpointer()); + + // Compute pointers to match string and capture string + __ lea(ebx, Operand(esi, edi, times_1, 0)); // Start of match. + __ add(edx, Operand(esi)); // Start of capture. + __ lea(ecx, Operand(eax, ebx, times_1, 0)); // End of match Label loop; __ bind(&loop); if (mode_ == ASCII) { __ movzx_b(eax, Operand(edx, 0)); - __ cmpb_al(Operand(edi, 0)); + __ cmpb_al(Operand(ebx, 0)); } else { ASSERT(mode_ == UC16); __ movzx_w(eax, Operand(edx, 0)); - __ cmpw_ax(Operand(edi, 0)); + __ cmpw_ax(Operand(ebx, 0)); } __ j(not_equal, &fail); + // Increment pointers into capture and match string. __ add(Operand(edx), Immediate(char_size())); - __ add(Operand(edi), Immediate(char_size())); - __ cmp(edi, Operand(ecx)); + __ add(Operand(ebx), Immediate(char_size())); + // Check if we have reached end of match area. + __ cmp(ebx, Operand(ecx)); __ j(below, &loop); __ jmp(&success); __ bind(&fail); - __ mov(edi, ebx); + // Restore backtrack stackpointer. + __ pop(backtrack_stackpointer()); BranchOrBacktrack(no_condition, on_no_match); __ bind(&success); - __ sub(edi, Operand(esi)); + // Move current character position to position after match. + __ mov(edi, ecx); + __ sub(Operand(edi), esi); + // Restore backtrack stackpointer. + __ pop(backtrack_stackpointer()); + __ bind(&fallthrough); } @@ -406,6 +457,7 @@ void RegExpMacroAssemblerIA32::CheckNotCharacterAfterMinusAnd( BranchOrBacktrack(not_equal, on_not_equal); } + bool RegExpMacroAssemblerIA32::CheckSpecialCharacterClass(uc16 type, int cp_offset, bool check_offset, @@ -428,7 +480,7 @@ bool RegExpMacroAssemblerIA32::CheckSpecialCharacterClass(uc16 type, // Check range 0x09..0x0d __ sub(Operand(current_character()), Immediate('\t')); __ cmp(current_character(), '\r' - '\t'); - BranchOrBacktrack(above_equal, on_no_match); + BranchOrBacktrack(above, on_no_match); __ bind(&success); return true; } @@ -446,7 +498,7 @@ bool RegExpMacroAssemblerIA32::CheckSpecialCharacterClass(uc16 type, BranchOrBacktrack(equal, on_no_match); __ sub(Operand(current_character()), Immediate('\t')); __ cmp(current_character(), '\r' - '\t'); - BranchOrBacktrack(below, on_no_match); + BranchOrBacktrack(below_equal, on_no_match); return true; } return false; @@ -459,7 +511,7 @@ bool RegExpMacroAssemblerIA32::CheckSpecialCharacterClass(uc16 type, } __ sub(Operand(current_character()), Immediate('0')); __ cmp(current_character(), '9' - '0'); - BranchOrBacktrack(greater_equal, on_no_match); + BranchOrBacktrack(above, on_no_match); return true; case 'D': // Match non ASCII-digits @@ -470,7 +522,7 @@ bool RegExpMacroAssemblerIA32::CheckSpecialCharacterClass(uc16 type, } __ sub(Operand(current_character()), Immediate('0')); __ cmp(current_character(), '9' - '0'); - BranchOrBacktrack(below, on_no_match); + BranchOrBacktrack(below_equal, on_no_match); return true; case '.': { // Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029) @@ -479,18 +531,18 @@ bool RegExpMacroAssemblerIA32::CheckSpecialCharacterClass(uc16 type, } else { LoadCurrentCharacterUnchecked(cp_offset, 1); } - // Compute hash value so exactly 0x0a and 0x0d become zero. - __ sub(Operand(current_character()), Immediate('\n')); - __ mov(eax, current_character()); - __ and_(current_character(), 0x01); - __ shr(eax, 1); - __ xor_(current_character(), Operand(eax)); - BranchOrBacktrack(equal, on_no_match); + __ xor_(Operand(current_character()), Immediate(0x01)); + // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c + __ sub(Operand(current_character()), Immediate(0x0b)); + __ cmp(current_character(), 0x0c - 0x0b); + BranchOrBacktrack(below_equal, on_no_match); if (mode_ == UC16) { // Compare original value to 0x2028 and 0x2029, using the already - // computed ((current_char - '\n') >> 1) in eax. - __ cmp(eax, (0x2028 - '\n') >> 1); - BranchOrBacktrack(equal, on_no_match); + // computed (current_char ^ 0x01 - 0x0b). I.e., check for + // 0x201d (0x2028 - 0x0b) or 0x201e. + __ sub(Operand(current_character()), Immediate(0x2028 - 0x0b)); + __ cmp(current_character(), 1); + BranchOrBacktrack(below_equal, on_no_match); } return true; } @@ -511,35 +563,6 @@ void RegExpMacroAssemblerIA32::DispatchHalfNibbleMap( Label* half_nibble_map, const Vector& destinations) { UNIMPLEMENTED(); - __ mov(eax, current_character()); - __ sub(Operand(eax), Immediate(start)); - - __ mov(ecx, eax); - __ shr(eax, 2); - // FIXME: ecx must hold address of map - __ movzx_b(eax, Operand(ecx, eax, times_1, 0)); - __ and_(ecx, 0x03); - __ add(ecx, Operand(ecx)); - __ shr(eax); // Shift right cl times - - Label second_bit_set, case_3, case_1; - __ test(eax, Immediate(0x02)); - __ j(not_zero, &second_bit_set); - __ test(eax, Immediate(0x01)); - __ j(not_zero, &case_1); - // Case 0: - __ jmp(destinations[0]); - __ bind(&case_1); - // Case 1: - __ jmp(destinations[1]); - __ bind(&second_bit_set); - __ test(eax, Immediate(0x01)); - __ j(not_zero, &case_3); - // Case 2 - __ jmp(destinations[2]); - __ bind(&case_3); - // Case 3: - __ jmp(destinations[3]); } @@ -548,17 +571,6 @@ void RegExpMacroAssemblerIA32::DispatchByteMap( Label* byte_map, const Vector& destinations) { UNIMPLEMENTED(); - - Label fallthrough; - __ mov(eax, current_character()); - __ sub(Operand(eax), Immediate(start)); - __ cmp(eax, 64); // FIXME: 64 = size of map. Found somehow?? - __ j(greater_equal, &fallthrough); - // TODO(lrn): ecx must hold address of map - __ movzx_b(eax, Operand(ecx, eax, times_1, 0)); - // jump table: jump to destinations[eax]; - - __ bind(&fallthrough); } @@ -567,16 +579,6 @@ void RegExpMacroAssemblerIA32::DispatchHighByteMap( Label* byte_map, const Vector& destinations) { UNIMPLEMENTED(); - - Label fallthrough; - __ mov(eax, current_character()); - __ shr(eax, 8); - __ sub(Operand(eax), Immediate(start)); - __ cmp(eax, destinations.length() - start); - __ j(greater_equal, &fallthrough); - - // TODO(lrn) jumptable: jump to destinations[eax] - __ bind(&fallthrough); } @@ -586,6 +588,7 @@ void RegExpMacroAssemblerIA32::EmitOrLink(Label* label) { void RegExpMacroAssemblerIA32::Fail() { + ASSERT(FAILURE == 0); // Return value for failure is zero. __ xor_(eax, Operand(eax)); // zero eax. __ jmp(&exit_label_); } @@ -602,6 +605,46 @@ Handle RegExpMacroAssemblerIA32::GetCode(Handle source) { __ push(esi); __ push(edi); __ push(ebx); // Callee-save on MacOS. + + // Check if we have space on the stack for registers. + Label retry_stack_check; + Label stack_limit_hit; + Label stack_ok; + + __ bind(&retry_stack_check); + ExternalReference stack_guard_limit = + ExternalReference::address_of_stack_guard_limit(); + __ mov(ecx, esp); + __ sub(ecx, Operand::StaticVariable(stack_guard_limit)); + // Handle it if the stack pointer is already below the stack limit. + __ j(below_equal, &stack_limit_hit, not_taken); + // Check if there is room for num_registers + ebp above the stack limit. + __ cmp(ecx, (num_registers_ + 1) * kPointerSize); + __ j(above_equal, &stack_ok, taken); + // Exit with exception. + __ mov(eax, EXCEPTION); + Label exit_without_leave; + __ jmp(&exit_without_leave); + + __ bind(&stack_limit_hit); + int num_arguments = 2; + FrameAlign(num_arguments); + __ mov(Operand(esp, 1 * kPointerSize), Immediate(self_)); + __ lea(eax, Operand(esp, -kPointerSize)); + __ mov(Operand(esp, 0 * kPointerSize), eax); + CallCFunction(FUNCTION_ADDR(&CheckStackGuardState), num_arguments); + __ or_(eax, Operand(eax)); + // If returned value is non-zero, the stack guard reports the actual + // stack limit being hit and an exception has already been raised. + // Otherwise it was a preemption and we just check the limit again. + __ j(equal, &retry_stack_check); + // Return value was non-zero. Exit with exception. + __ mov(eax, EXCEPTION); + __ jmp(&exit_without_leave); + + __ bind(&stack_ok); + + // Allocate space on stack for registers. __ enter(Immediate(num_registers_ * kPointerSize)); // Load string length. __ mov(esi, Operand(ebp, kInputEndOffset)); @@ -617,15 +660,32 @@ Handle RegExpMacroAssemblerIA32::GetCode(Handle source) { __ add(esi, Operand(edx)); if (num_saved_registers_ > 0) { // Fill saved registers with initial value = start offset - 1 - __ mov(ecx, -num_saved_registers_); - __ mov(eax, Operand(edi)); - __ sub(Operand(eax), Immediate(char_size())); + // Fill in stack push order, to avoid accessing across an unwritten + // page (a problem on Windows). + const int kRegisterZeroEBPOffset = -1; + __ mov(ecx, kRegisterZeroEBPOffset); + // Set eax to address of char before start of input. + __ lea(eax, Operand(edi, -char_size())); Label init_loop; __ bind(&init_loop); __ mov(Operand(ebp, ecx, times_4, +0), eax); - __ inc(ecx); - __ j(not_equal, &init_loop); + __ sub(Operand(ecx), Immediate(1)); + __ cmp(ecx, -num_saved_registers_); + __ j(greater_equal, &init_loop); + } + // Ensure that we have written to each stack page. Skipping a page on + // Windows can cause segmentation faults. Assuming page size is 4k. + const int kPageSize = 4096; + const int kRegistersPerPage = kPageSize / kPointerSize; + for (int i = num_saved_registers_ + kRegistersPerPage - 1; + i < num_registers_; + i += kRegistersPerPage) { + __ mov(register_location(i), eax); // One write every page. } + + + // Initialize backtrack stack pointer. + __ mov(backtrack_stackpointer(), Operand(ebp, kStackHighEnd)); // Load previous char as initial value of current-character. Label at_start; __ cmp(Operand(ebp, kAtStart), Immediate(0)); @@ -655,11 +715,12 @@ Handle RegExpMacroAssemblerIA32::GetCode(Handle source) { __ mov(Operand(ebx, i * kPointerSize), eax); } } - __ mov(eax, Immediate(1)); + __ mov(eax, Immediate(SUCCESS)); } // Exit and return eax __ bind(&exit_label_); __ leave(); + __ bind(&exit_without_leave); // For exiting before doing enter. __ pop(ebx); __ pop(edi); __ pop(esi); @@ -671,16 +732,16 @@ Handle RegExpMacroAssemblerIA32::GetCode(Handle source) { Backtrack(); } + Label exit_with_exception; + // Preempt-code if (check_preempt_label_.is_linked()) { __ bind(&check_preempt_label_); - // TODO(lrn): call C function to check the stack guard and return current - // stack state (0 = ok, positive = out of stack, negative = preempt). - // Then dispatch to an action depending on state, and loop. + + __ push(backtrack_stackpointer()); __ push(edi); Label retry; - Label stack_overflow; __ bind(&retry); int num_arguments = 2; @@ -689,26 +750,57 @@ Handle RegExpMacroAssemblerIA32::GetCode(Handle source) { __ lea(eax, Operand(esp, -kPointerSize)); __ mov(Operand(esp, 0 * kPointerSize), eax); CallCFunction(FUNCTION_ADDR(&CheckStackGuardState), num_arguments); + // Return value must be zero. We cannot have a stack overflow at + // this point, since we checked the stack on entry and haven't + // pushed anything since, that we haven't also popped again. ExternalReference stack_guard_limit = ExternalReference::address_of_stack_guard_limit(); - - __ or_(eax, Operand(eax)); - __ j(not_equal, &stack_overflow); - + // Check if we are still preempted. __ cmp(esp, Operand::StaticVariable(stack_guard_limit)); __ j(below_equal, &retry); __ pop(edi); + __ pop(backtrack_stackpointer()); // String might have moved: Recompute esi from scratch. - __ mov(esi, Operand(esp, kInputBuffer)); + __ mov(esi, Operand(ebp, kInputBuffer)); __ mov(esi, Operand(esi, 0)); - __ add(esi, Operand(esp, kInputEndOffset)); + __ add(esi, Operand(ebp, kInputEndOffset)); + SafeReturn(); + } + + // Backtrack stack overflow code. + if (stack_overflow_label_.is_linked()) { + __ bind(&stack_overflow_label_); + // Reached if the backtrack-stack limit has been hit. + + Label grow_failed; + // Save registers before calling C function + __ push(esi); + __ push(edi); + + // Call GrowStack(backtrack_stackpointer()) + int num_arguments = 1; + FrameAlign(num_arguments); + __ mov(Operand(esp, 0), backtrack_stackpointer()); + CallCFunction(FUNCTION_ADDR(&GrowStack), num_arguments); + // If return NULL, we have failed to grow the stack, and + // must exit with a stack-overflow exception. + __ or_(eax, Operand(eax)); + __ j(equal, &exit_with_exception); + // Otherwise use return value as new stack pointer. + __ mov(backtrack_stackpointer(), eax); + // Restore saved registers and continue. + __ pop(edi); + __ pop(esi); SafeReturn(); + } - __ bind(&stack_overflow); - // Exit with result -1 to signal thrown exception. - __ mov(eax, -1); + if (exit_with_exception.is_linked()) { + // If any of the code above needed to exit with an exception. + __ bind(&exit_with_exception); + // Exit with Result EXCEPTION(-1) to signal thrown exception. + __ mov(eax, EXCEPTION); __ jmp(&exit_label_); } @@ -769,28 +861,32 @@ void RegExpMacroAssemblerIA32::LoadCurrentCharacter(int cp_offset, void RegExpMacroAssemblerIA32::PopCurrentPosition() { - __ pop(edi); + Pop(edi); } void RegExpMacroAssemblerIA32::PopRegister(int register_index) { - __ pop(register_location(register_index)); + Pop(eax); + __ mov(register_location(register_index), eax); } void RegExpMacroAssemblerIA32::PushBacktrack(Label* label) { - __ push(Immediate::CodeRelativeOffset(label)); + Push(Immediate::CodeRelativeOffset(label)); CheckStackLimit(); } void RegExpMacroAssemblerIA32::PushCurrentPosition() { - __ push(edi); + Push(edi); } -void RegExpMacroAssemblerIA32::PushRegister(int register_index) { - __ push(register_location(register_index)); +void RegExpMacroAssemblerIA32::PushRegister(int register_index, + StackCheckFlag check_stack_limit) { + __ mov(eax, register_location(register_index)); + Push(eax); + if (check_stack_limit) CheckStackLimit(); } @@ -800,7 +896,7 @@ void RegExpMacroAssemblerIA32::ReadCurrentPositionFromRegister(int reg) { void RegExpMacroAssemblerIA32::ReadStackPointerFromRegister(int reg) { - __ mov(esp, register_location(reg)); + __ mov(backtrack_stackpointer(), register_location(reg)); } @@ -827,7 +923,7 @@ void RegExpMacroAssemblerIA32::WriteCurrentPositionToRegister(int reg, void RegExpMacroAssemblerIA32::WriteStackPointerToRegister(int reg) { - __ mov(register_location(reg), esp); + __ mov(register_location(reg), backtrack_stackpointer()); } @@ -837,6 +933,38 @@ void RegExpMacroAssemblerIA32::WriteStackPointerToRegister(int reg) { static unibrow::Mapping canonicalize; +RegExpMacroAssemblerIA32::Result RegExpMacroAssemblerIA32::Execute( + Code* code, + Address* input, + int start_offset, + int end_offset, + int* output, + bool at_start) { + typedef int (*matcher)(Address*, int, int, int*, int, void*); + matcher matcher_func = FUNCTION_CAST(code->entry()); + + int at_start_val = at_start ? 1 : 0; + + // Ensure that the minimum stack has been allocated. + RegExpStack stack; + void* stack_top = RegExpStack::stack_top(); + + int result = matcher_func(input, + start_offset, + end_offset, + output, + at_start_val, + stack_top); + + if (result < 0 && !Top::has_pending_exception()) { + // We detected a stack overflow in RegExp code, but haven't created + // the exception yet. + Top::StackOverflow(); + } + return (result < 0) ? EXCEPTION : (result ? SUCCESS : FAILURE); +} + + int RegExpMacroAssemblerIA32::CaseInsensitiveCompareUC16(uc16** buffer, int byte_offset1, int byte_offset2, @@ -879,11 +1007,10 @@ int RegExpMacroAssemblerIA32::CheckStackGuardState(Address return_address, // Prepare for possible GC. Handle code_handle(re_code); -#ifdef DEBUG - CHECK(re_code->instruction_start() <= return_address); - CHECK(return_address <= + + ASSERT(re_code->instruction_start() <= return_address); + ASSERT(return_address <= re_code->instruction_start() + re_code->instruction_size()); -#endif Object* result = Execution::HandleStackGuardInterrupt(); @@ -899,6 +1026,17 @@ int RegExpMacroAssemblerIA32::CheckStackGuardState(Address return_address, } +Address RegExpMacroAssemblerIA32::GrowStack(Address stack_top) { + size_t size = RegExpStack::stack_capacity(); + Address old_stack_end = RegExpStack::stack_top(); + Address new_stack_end = RegExpStack::EnsureCapacity(size * 2); + if (new_stack_end == NULL) { + return NULL; + } + return stack_top + (new_stack_end - old_stack_end); +} + + Operand RegExpMacroAssemblerIA32::register_location(int register_index) { ASSERT(register_index < (1<<30)); if (num_registers_ <= register_index) { @@ -908,16 +1046,6 @@ Operand RegExpMacroAssemblerIA32::register_location(int register_index) { } -Register RegExpMacroAssemblerIA32::current_character() { - return edx; -} - - -size_t RegExpMacroAssemblerIA32::char_size() { - return static_cast(mode_); -} - - void RegExpMacroAssemblerIA32::CheckPosition(int cp_offset, Label* on_outside_input) { __ cmp(edi, -cp_offset * char_size()); @@ -926,7 +1054,8 @@ void RegExpMacroAssemblerIA32::CheckPosition(int cp_offset, void RegExpMacroAssemblerIA32::BranchOrBacktrack(Condition condition, - Label* to) { + Label* to, + Hint hint) { if (condition < 0) { // No condition if (to == NULL) { Backtrack(); @@ -936,10 +1065,10 @@ void RegExpMacroAssemblerIA32::BranchOrBacktrack(Condition condition, return; } if (to == NULL) { - __ j(condition, &backtrack_label_); + __ j(condition, &backtrack_label_, hint); return; } - __ j(condition, to); + __ j(condition, to, hint); } @@ -952,25 +1081,60 @@ void RegExpMacroAssemblerIA32::SafeCall(Label* to) { void RegExpMacroAssemblerIA32::SafeReturn() { - __ pop(ecx); - __ add(Operand(ecx), Immediate(self_)); - __ jmp(Operand(ecx)); + __ pop(ebx); + __ add(Operand(ebx), Immediate(self_)); + __ jmp(Operand(ebx)); +} + + +void RegExpMacroAssemblerIA32::Push(Register source) { + ASSERT(!source.is(backtrack_stackpointer())); + // Notice: This updates flags, unlike normal Push. + __ sub(Operand(backtrack_stackpointer()), Immediate(kPointerSize)); + __ mov(Operand(backtrack_stackpointer(), 0), source); +} + + +void RegExpMacroAssemblerIA32::Push(Immediate value) { + // Notice: This updates flags, unlike normal Push. + __ sub(Operand(backtrack_stackpointer()), Immediate(kPointerSize)); + __ mov(Operand(backtrack_stackpointer(), 0), value); +} + + +void RegExpMacroAssemblerIA32::Pop(Register target) { + ASSERT(!target.is(backtrack_stackpointer())); + __ mov(target, Operand(backtrack_stackpointer(), 0)); + // Notice: This updates flags, unlike normal Pop. + __ add(Operand(backtrack_stackpointer()), Immediate(kPointerSize)); +} + + +void RegExpMacroAssemblerIA32::CheckPreemption() { + // Check for preemption. + Label no_preempt; + ExternalReference stack_guard_limit = + ExternalReference::address_of_stack_guard_limit(); + __ cmp(esp, Operand::StaticVariable(stack_guard_limit)); + __ j(above, &no_preempt, taken); + + SafeCall(&check_preempt_label_); + + __ bind(&no_preempt); } void RegExpMacroAssemblerIA32::CheckStackLimit() { if (FLAG_check_stack) { - // Check for preemption first. - Label no_preempt; - // Check for preemption. - ExternalReference stack_guard_limit = - ExternalReference::address_of_stack_guard_limit(); - __ cmp(esp, Operand::StaticVariable(stack_guard_limit)); - __ j(above, &no_preempt, taken); + Label no_stack_overflow; + ExternalReference stack_limit = + ExternalReference::address_of_regexp_stack_limit(); + __ cmp(backtrack_stackpointer(), Operand::StaticVariable(stack_limit)); + __ j(above, &no_stack_overflow); - SafeCall(&check_preempt_label_); + SafeCall(&stack_overflow_label_); - __ bind(&no_preempt); + __ bind(&no_stack_overflow); } } diff --git a/src/regexp-macro-assembler-ia32.h b/src/regexp-macro-assembler-ia32.h index 2f716d6ca..1fdbf9870 100644 --- a/src/regexp-macro-assembler-ia32.h +++ b/src/regexp-macro-assembler-ia32.h @@ -38,6 +38,7 @@ class RegExpMacroAssemblerIA32: public RegExpMacroAssembler { RegExpMacroAssemblerIA32(Mode mode, int registers_to_save); virtual ~RegExpMacroAssemblerIA32(); + virtual int stack_limit_slack(); virtual void AdvanceCurrentPosition(int by); virtual void AdvanceRegister(int reg, int by); virtual void Backtrack(); @@ -53,6 +54,8 @@ class RegExpMacroAssemblerIA32: public RegExpMacroAssembler { int cp_offset, Label* on_failure, bool check_end_of_string); + // A "greedy loop" is a loop that is both greedy and with a simple + // body. It has a particularly simple implementation. virtual void CheckGreedyLoop(Label* on_tos_equals_current_position); virtual void CheckNotAtStart(Label* on_not_at_start); virtual void CheckNotBackReference(int start_reg, Label* on_no_match); @@ -96,7 +99,8 @@ class RegExpMacroAssemblerIA32: public RegExpMacroAssembler { virtual void PopRegister(int register_index); virtual void PushBacktrack(Label* label); virtual void PushCurrentPosition(); - virtual void PushRegister(int register_index); + virtual void PushRegister(int register_index, + StackCheckFlag check_stack_limit); virtual void ReadCurrentPositionFromRegister(int reg); virtual void ReadStackPointerFromRegister(int reg); virtual void SetRegister(int register_index, int to); @@ -104,26 +108,15 @@ class RegExpMacroAssemblerIA32: public RegExpMacroAssembler { virtual void WriteCurrentPositionToRegister(int reg, int cp_offset); virtual void WriteStackPointerToRegister(int reg); - template - static inline Result Execute(Code* code, - T** input, - int start_offset, - int end_offset, - int* output, - bool at_start) { - typedef int (*matcher)(T**, int, int, int*, int); - matcher matcher_func = FUNCTION_CAST(code->entry()); - int at_start_val = at_start ? 1 : 0; - int result = matcher_func(input, - start_offset, - end_offset, - output, - at_start_val); - return (result < 0) ? EXCEPTION : (result ? SUCCESS : FAILURE); - } + static Result Execute(Code* code, + Address* input, + int start_offset, + int end_offset, + int* output, + bool at_start); private: - // Offsets from ebp of arguments to function. + // Offsets from ebp of arguments to function and stored registers. static const int kBackup_ebx = sizeof(uint32_t); static const int kBackup_edi = kBackup_ebx + sizeof(uint32_t); static const int kBackup_esi = kBackup_edi + sizeof(uint32_t); @@ -133,24 +126,30 @@ class RegExpMacroAssemblerIA32: public RegExpMacroAssembler { static const int kInputEndOffset = kInputStartOffset + sizeof(uint32_t); static const int kRegisterOutput = kInputEndOffset + sizeof(uint32_t); static const int kAtStart = kRegisterOutput + sizeof(uint32_t); + static const int kStackHighEnd = kAtStart + sizeof(uint32_t); // Initial size of code buffer. static const size_t kRegExpCodeSize = 1024; // Initial size of constant buffers allocated during compilation. static const int kRegExpConstantsSize = 256; - // Only unroll loops up to this length. TODO(lrn): Actually use this. + // Only unroll loops up to this length. static const int kMaxInlineStringTests = 32; // Compares two-byte strings case insensitively. + // Called from generated RegExp code. static int CaseInsensitiveCompareUC16(uc16** buffer, int byte_offset1, int byte_offset2, size_t byte_length); - void LoadCurrentCharacterUnchecked(int cp_offset, int characters); + // Load a number of characters at the given offset from the + // current position, into the current-character register. + void LoadCurrentCharacterUnchecked(int cp_offset, int character_count); - // Adds code that checks whether preemption has been requested - // (and checks if we have hit the stack limit too). + // Check whether preemption has been requested. + void CheckPreemption(); + + // Check whether we are exceeding the stack limit on the backtrack stack. void CheckStackLimit(); // Called from RegExp if the stack-guard is triggered. @@ -158,6 +157,12 @@ class RegExpMacroAssemblerIA32: public RegExpMacroAssembler { // returning. static int CheckStackGuardState(Address return_address, Code* re_code); + // Called from RegExp if the backtrack stack limit is hit. + // Tries to expand the stack. Returns the new stack-top pointer if + // successful, or 0 if unable to grow the stack. + // This function must not trigger a garbage collection. + static Address GrowStack(Address stack_top); + // Checks whether the given offset from the current position is before // the end of the string. void CheckPosition(int cp_offset, Label* on_outside_input); @@ -166,14 +171,18 @@ class RegExpMacroAssemblerIA32: public RegExpMacroAssembler { Operand register_location(int register_index); // The register containing the current character after LoadCurrentCharacter. - Register current_character(); + inline Register current_character() { return edx; } + + // The register containing the backtrack stack top. Provides a meaningful + // name to the register. + inline Register backtrack_stackpointer() { return ecx; } // Byte size of chars in the string to match (decided by the Mode argument) - size_t char_size(); + inline size_t char_size() { return static_cast(mode_); } // Equivalent to a conditional branch to the label, unless the label // is NULL, in which case it is a conditional Backtrack. - void BranchOrBacktrack(Condition condition, Label* to); + void BranchOrBacktrack(Condition condition, Label* to, Hint hint = no_hint); // Load the address of a "constant buffer" (a slice of a byte array) // into a register. The address is computed from the ByteArray* address @@ -182,30 +191,50 @@ class RegExpMacroAssemblerIA32: public RegExpMacroAssembler { // Call and return internally in the generated code in a way that // is GC-safe (i.e., doesn't leave absolute code addresses on the stack) - void SafeCall(Label* to); - void SafeReturn(); + inline void SafeCall(Label* to); + inline void SafeReturn(); + + // Pushes the value of a register on the backtrack stack. Decrements the + // stack pointer (ecx) by a word size and stores the register's value there. + inline void Push(Register source); + + // Pushes a value on the backtrack stack. Decrements the stack pointer (ecx) + // by a word size and stores the value there. + inline void Push(Immediate value); + + // Pops a value from the backtrack stack. Reads the word at the stack pointer + // (ecx) and increments it by a word size. + inline void Pop(Register target); // Before calling a C-function from generated code, align arguments on stack. // After aligning the frame, arguments must be stored in esp[0], esp[4], // etc., not pushed. The argument count assumes all arguments are word sized. - void FrameAlign(int num_arguments); + // Some compilers/platforms require the stack to be aligned when calling + // C++ code. + inline void FrameAlign(int num_arguments); + // Calls a C function and cleans up the space for arguments allocated // by FrameAlign. The called function is not allowed to trigger a garbage // collection, since that might move the code and invalidate the return - // address - void CallCFunction(Address function_address, int num_arguments); + // address (unless this is somehow accounted for). + inline void CallCFunction(Address function_address, int num_arguments); MacroAssembler* masm_; + // Constant buffer provider. Allocates external storage for storing // constants. ByteArrayProvider constants_; + // Which mode to generate code for (ASCII or UTF16). Mode mode_; + // One greater than maximal register index actually used. int num_registers_; + // Number of registers to output at the end (the saved registers // are always 0..num_saved_registers_-1) int num_saved_registers_; + // Labels used internally. Label entry_label_; Label start_label_; @@ -213,6 +242,8 @@ class RegExpMacroAssemblerIA32: public RegExpMacroAssembler { Label backtrack_label_; Label exit_label_; Label check_preempt_label_; + Label stack_overflow_label_; + // Handle used to represent the generated code object itself. Handle self_; }; diff --git a/src/regexp-macro-assembler-irregexp.cc b/src/regexp-macro-assembler-irregexp.cc index e2bed27ee..d962a739b 100644 --- a/src/regexp-macro-assembler-irregexp.cc +++ b/src/regexp-macro-assembler-irregexp.cc @@ -89,7 +89,9 @@ void RegExpMacroAssemblerIrregexp::PopRegister(int register_index) { } -void RegExpMacroAssemblerIrregexp::PushRegister(int register_index) { +void RegExpMacroAssemblerIrregexp::PushRegister( + int register_index, + StackCheckFlag check_stack_limit) { ASSERT(register_index >= 0); Emit(BC_PUSH_REGISTER); Emit(register_index); diff --git a/src/regexp-macro-assembler-irregexp.h b/src/regexp-macro-assembler-irregexp.h index 1167b1d53..b2fba37cf 100644 --- a/src/regexp-macro-assembler-irregexp.h +++ b/src/regexp-macro-assembler-irregexp.h @@ -48,6 +48,8 @@ class RegExpMacroAssemblerIrregexp: public RegExpMacroAssembler { // upon destruction of the assembler. explicit RegExpMacroAssemblerIrregexp(Vector); virtual ~RegExpMacroAssemblerIrregexp(); + // The byte-code interpreter checks on each push anyway. + virtual int stack_limit_slack() { return 1; } virtual void Bind(Label* label); virtual void EmitOrLink(Label* label); virtual void AdvanceCurrentPosition(int by); // Signed cp change. @@ -59,7 +61,8 @@ class RegExpMacroAssemblerIrregexp: public RegExpMacroAssembler { virtual void Succeed(); virtual void Fail(); virtual void PopRegister(int register_index); - virtual void PushRegister(int register_index); + virtual void PushRegister(int register_index, + StackCheckFlag check_stack_limit); virtual void AdvanceRegister(int reg, int by); // r[reg] += by. virtual void SetRegister(int register_index, int to); virtual void WriteCurrentPositionToRegister(int reg, int cp_offset); diff --git a/src/regexp-macro-assembler-tracer.cc b/src/regexp-macro-assembler-tracer.cc index 2eb7ec58f..c733a2fcc 100644 --- a/src/regexp-macro-assembler-tracer.cc +++ b/src/regexp-macro-assembler-tracer.cc @@ -95,7 +95,8 @@ void RegExpMacroAssemblerTracer::GoTo(Label* label) { void RegExpMacroAssemblerTracer::PushBacktrack(Label* label) { - PrintF(" PushBacktrack(label[%08x]);\n", label); + PrintF(" PushBacktrack(label[%08x]);\n", + label); assembler_->PushBacktrack(label); } @@ -118,9 +119,13 @@ void RegExpMacroAssemblerTracer::PopRegister(int register_index) { } -void RegExpMacroAssemblerTracer::PushRegister(int register_index) { - PrintF(" PushRegister(register=%d);\n", register_index); - assembler_->PushRegister(register_index); +void RegExpMacroAssemblerTracer::PushRegister( + int register_index, + StackCheckFlag check_stack_limit) { + PrintF(" PushRegister(register=%d, %s);\n", + register_index, + check_stack_limit ? "check stack limit" : ""); + assembler_->PushRegister(register_index, check_stack_limit); } diff --git a/src/regexp-macro-assembler-tracer.h b/src/regexp-macro-assembler-tracer.h index f5b8851cf..7a7a4017a 100644 --- a/src/regexp-macro-assembler-tracer.h +++ b/src/regexp-macro-assembler-tracer.h @@ -35,6 +35,7 @@ class RegExpMacroAssemblerTracer: public RegExpMacroAssembler { public: explicit RegExpMacroAssemblerTracer(RegExpMacroAssembler* assembler); virtual ~RegExpMacroAssemblerTracer(); + virtual int stack_limit_slack() { return assembler_->stack_limit_slack(); } virtual void AdvanceCurrentPosition(int by); // Signed cp change. virtual void AdvanceRegister(int reg, int by); // r[reg] += by. @@ -98,7 +99,8 @@ class RegExpMacroAssemblerTracer: public RegExpMacroAssembler { virtual void PopRegister(int register_index); virtual void PushBacktrack(Label* label); virtual void PushCurrentPosition(); - virtual void PushRegister(int register_index); + virtual void PushRegister(int register_index, + StackCheckFlag check_stack_limit); virtual void ReadCurrentPositionFromRegister(int reg); virtual void ReadStackPointerFromRegister(int reg); virtual void SetRegister(int register_index, int to); diff --git a/src/regexp-macro-assembler.cc b/src/regexp-macro-assembler.cc index 34925f566..913ac6462 100644 --- a/src/regexp-macro-assembler.cc +++ b/src/regexp-macro-assembler.cc @@ -68,10 +68,11 @@ ArraySlice ByteArrayProvider::GetBuffer(unsigned int size, return ArraySlice(current_byte_array_, free_offset); } + template ArraySlice ByteArrayProvider::GetBuffer(Vector values) { ArraySlice slice = GetBuffer(values.length(), sizeof(T)); memcpy(slice.location(), values.start(), values.length() * sizeof(T)); return slice; } -} } +} } // namespace v8::internal diff --git a/src/regexp-macro-assembler.h b/src/regexp-macro-assembler.h index 878a29715..4b76cab2d 100644 --- a/src/regexp-macro-assembler.h +++ b/src/regexp-macro-assembler.h @@ -30,7 +30,6 @@ namespace v8 { namespace internal { - struct DisjunctDecisionRow { RegExpCharacterClass cc; Label* on_match; @@ -42,12 +41,24 @@ class RegExpMacroAssembler { enum IrregexpImplementation { kIA32Implementation, kARMImplementation, - kBytecodeImplementation}; + kBytecodeImplementation + }; + + enum StackCheckFlag { + kNoStackLimitCheck = false, + kCheckStackLimit = true + }; RegExpMacroAssembler(); virtual ~RegExpMacroAssembler(); + // The maximal number of pushes between stack checks. Users must supply + // kCheckStackLimit flag to push operations (instead of kNoStackLimitCheck) + // at least once for every stack_limit() pushes that are executed. + virtual int stack_limit_slack() = 0; virtual void AdvanceCurrentPosition(int by) = 0; // Signed cp change. virtual void AdvanceRegister(int reg, int by) = 0; // r[reg] += by. + // Continues execution from the position pushed on the top of the backtrack + // stack by an earlier PushBacktrack(Label*). virtual void Backtrack() = 0; virtual void Bind(Label* label) = 0; // Check the current character against a bitmap. The range of the current @@ -145,9 +156,12 @@ class RegExpMacroAssembler { int characters = 1) = 0; virtual void PopCurrentPosition() = 0; virtual void PopRegister(int register_index) = 0; + // Pushes the label on the backtrack stack, so that a following Backtrack + // will go to this label. Always checks the backtrack stack limit. virtual void PushBacktrack(Label* label) = 0; virtual void PushCurrentPosition() = 0; - virtual void PushRegister(int register_index) = 0; + virtual void PushRegister(int register_index, + StackCheckFlag check_stack_limit) = 0; virtual void ReadCurrentPositionFromRegister(int reg) = 0; virtual void ReadStackPointerFromRegister(int reg) = 0; virtual void SetRegister(int register_index, int to) = 0; diff --git a/src/regexp-stack.cc b/src/regexp-stack.cc new file mode 100644 index 000000000..05daa58d4 --- /dev/null +++ b/src/regexp-stack.cc @@ -0,0 +1,94 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" +#include "top.h" +#include "regexp-stack.h" + +namespace v8 { namespace internal { + +RegExpStack::RegExpStack() { + // Initialize, if not already initialized. + RegExpStack::EnsureCapacity(0); +} + + +RegExpStack::~RegExpStack() { + // Reset the buffer if it has grown. + RegExpStack::Reset(); +} + + +char* RegExpStack::ArchiveStack(char* to) { + size_t size = sizeof(thread_local_); + memcpy(reinterpret_cast(to), + &thread_local_, + size); + thread_local_ = ThreadLocal(); + return to + size; +} + + +char* RegExpStack::RestoreStack(char* from) { + size_t size = sizeof(thread_local_); + memcpy(&thread_local_, reinterpret_cast(from), size); + return from + size; +} + + +void RegExpStack::Reset() { + if (thread_local_.memory_size_ > kMinimumStackSize) { + DeleteArray(thread_local_.memory_); + thread_local_ = ThreadLocal(); + } +} + + +Address RegExpStack::EnsureCapacity(size_t size) { + if (size > kMaximumStackSize) return NULL; + if (size < kMinimumStackSize) size = kMinimumStackSize; + if (thread_local_.memory_size_ < size) { + Address new_memory = NewArray(size); + if (thread_local_.memory_size_ > 0) { + // Copy original memory into top of new memory. + memcpy(reinterpret_cast( + new_memory + size - thread_local_.memory_size_), + reinterpret_cast(thread_local_.memory_), + thread_local_.memory_size_); + DeleteArray(thread_local_.memory_); + } + thread_local_.memory_ = new_memory; + thread_local_.memory_size_ = size; + thread_local_.limit_ = new_memory + kStackLimitSlack * kPointerSize; + } + return thread_local_.memory_ + thread_local_.memory_size_; +} + + +RegExpStack::ThreadLocal RegExpStack::thread_local_; + +}} // namespace v8::internal diff --git a/src/regexp-stack.h b/src/regexp-stack.h new file mode 100644 index 000000000..43f8f3d84 --- /dev/null +++ b/src/regexp-stack.h @@ -0,0 +1,106 @@ +// Copyright 2009 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef REGEXP_STACK_H_ +#define REGEXP_STACK_H_ + +namespace v8 { namespace internal { + +// Maintains a per-v8thread stack area that can be used by irregexp +// implementation for its backtracking stack. +// Since there is only one stack area, the Irregexp implementation is not +// re-entrant. I.e., no regular expressions may be executed in the same thread +// during a preempted Irregexp execution. +class RegExpStack { + public: + // Number of allocated locations on the stack below the limit. + // No sequence of pushes must be longer that this without doing a stack-limit + // check. + static const int kStackLimitSlack = 32; + + // Create and delete an instance to control the life-time of a growing stack. + RegExpStack(); // Initializes the stack memory area if necessary. + ~RegExpStack(); // Releases the stack if it has grown. + + // Gives the top of the memory used as stack. + static Address stack_top() { + ASSERT(thread_local_.memory_size_ != 0); + return thread_local_.memory_ + thread_local_.memory_size_; + } + + // The total size of the memory allocated for the stack. + static size_t stack_capacity() { return thread_local_.memory_size_; } + + // If the stack pointer gets below the limit, we should react and + // either grow the stack or report an out-of-stack exception. + // There is only a limited number of locations below the stack limit, + // so users of the stack should check the stack limit during any + // sequence of pushes longer that this. + static Address* limit_address() { return &(thread_local_.limit_); } + + // Ensures that there is a memory area with at least the specified size. + // If passing zero, the default/minimum size buffer is allocated. + static Address EnsureCapacity(size_t size); + + // Thread local archiving. + static size_t ArchiveSpacePerThread() { return sizeof(thread_local_); } + static char* ArchiveStack(char* to); + static char* RestoreStack(char* from); + + private: + // Artificial limit used when no memory has been allocated. + static const uint32_t kMemoryTop = 0xffffffff; + + // Minimal size of allocated stack area. + static const size_t kMinimumStackSize = 1 * KB; + + // Maximal size of allocated stack area. + static const size_t kMaximumStackSize = 256 * KB; + + // Structure holding the allocated memory, size and limit. + struct ThreadLocal { + ThreadLocal() + : memory_(NULL), + memory_size_(0), + limit_(reinterpret_cast
(kMemoryTop)) {} + // If memory_size_ > 0 then memory_ must be non-NULL. + Address memory_; + size_t memory_size_; + Address limit_; + }; + + // Resets the buffer if it has grown beyond the default/minimum size. + // After this, the buffer is either the default size, or it is empty, so + // you have to call EnsureCapacity before using it again. + static void Reset(); + + static ThreadLocal thread_local_; +}; + +}} // namespace v8::internal + +#endif /* REGEXP_STACK_H_ */ diff --git a/src/v8threads.cc b/src/v8threads.cc index 6b7533b6b..0a6c7c152 100644 --- a/src/v8threads.cc +++ b/src/v8threads.cc @@ -31,6 +31,7 @@ #include "debug.h" #include "execution.h" #include "v8threads.h" +#include "regexp-stack.h" namespace v8 { @@ -125,6 +126,7 @@ bool ThreadManager::RestoreThread() { from = Top::RestoreThread(from); from = Debug::RestoreDebug(from); from = StackGuard::RestoreStackGuard(from); + from = RegExpStack::RestoreStack(from); Thread::SetThreadLocal(thread_state_key, NULL); state->Unlink(); state->LinkInto(ThreadState::FREE_LIST); @@ -149,7 +151,8 @@ static int ArchiveSpacePerThread() { return HandleScopeImplementer::ArchiveSpacePerThread() + Top::ArchiveSpacePerThread() + Debug::ArchiveSpacePerThread() + - StackGuard::ArchiveSpacePerThread(); + StackGuard::ArchiveSpacePerThread() + + RegExpStack::ArchiveSpacePerThread(); } @@ -230,6 +233,7 @@ void ThreadManager::EagerlyArchiveThread() { to = Top::ArchiveThread(to); to = Debug::ArchiveDebug(to); to = StackGuard::ArchiveStackGuard(to); + to = RegExpStack::ArchiveStack(to); lazily_archived_thread_.Initialize(ThreadHandle::INVALID); lazily_archived_thread_state_ = NULL; } diff --git a/test/cctest/test-regexp.cc b/test/cctest/test-regexp.cc index 89ea9abb4..bb94d350c 100644 --- a/test/cctest/test-regexp.cc +++ b/test/cctest/test-regexp.cc @@ -598,7 +598,7 @@ TEST(MacroAssembler) { foo_chars[2] = 'o'; Vector foo(foo_chars, 3); m.SetRegister(4, 42); - m.PushRegister(4); + m.PushRegister(4, RegExpMacroAssembler::kNoStackLimitCheck); m.AdvanceRegister(4, 42); m.GoTo(&start); m.Fail(); @@ -666,6 +666,40 @@ class ContextInitializer { v8::internal::StackGuard stack_guard_; }; +// Helper functions for calling the Execute method. +template +static RegExpMacroAssemblerIA32::Result ExecuteIA32(Code* code, + const T** input, + int start_offset, + int end_offset, + int* captures, + bool at_start) { + return RegExpMacroAssemblerIA32::Execute( + code, + reinterpret_cast( + reinterpret_cast(const_cast(input))), + start_offset, + end_offset, + captures, + at_start); +} + +template +static RegExpMacroAssemblerIA32::Result ExecuteIA32(Code* code, + T** input, + int start_offset, + int end_offset, + int* captures, + bool at_start) { + return RegExpMacroAssemblerIA32::Execute( + code, + reinterpret_cast(reinterpret_cast(input)), + start_offset, + end_offset, + captures, + at_start); +} + TEST(MacroAssemblerIA32Success) { v8::V8::Initialize(); @@ -687,12 +721,12 @@ TEST(MacroAssemblerIA32Success) { int end_offset = start_offset + seq_input->length(); RegExpMacroAssemblerIA32::Result result = - RegExpMacroAssemblerIA32::Execute(*code, - seq_input.location(), - start_offset, - end_offset, - captures, - true); + ExecuteIA32(*code, + seq_input.location(), + start_offset, + end_offset, + captures, + true); CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result); CHECK_EQ(-1, captures[0]); @@ -732,12 +766,12 @@ TEST(MacroAssemblerIA32Simple) { int end_offset = start_offset + seq_input->length(); RegExpMacroAssemblerIA32::Result result = - RegExpMacroAssemblerIA32::Execute(*code, - seq_input.location(), - start_offset, - end_offset, - captures, - true); + ExecuteIA32(*code, + seq_input.location(), + start_offset, + end_offset, + captures, + true); CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result); CHECK_EQ(0, captures[0]); @@ -751,12 +785,12 @@ TEST(MacroAssemblerIA32Simple) { start_offset = start_adr - reinterpret_cast
(*seq_input); end_offset = start_offset + seq_input->length(); - result = RegExpMacroAssemblerIA32::Execute(*code, - seq_input.location(), - start_offset, - end_offset, - captures, - true); + result = ExecuteIA32(*code, + seq_input.location(), + start_offset, + end_offset, + captures, + true); CHECK_EQ(RegExpMacroAssemblerIA32::FAILURE, result); } @@ -794,12 +828,12 @@ TEST(MacroAssemblerIA32SimpleUC16) { int end_offset = start_offset + seq_input->length() * sizeof(uc16); RegExpMacroAssemblerIA32::Result result = - RegExpMacroAssemblerIA32::Execute(*code, - seq_input.location(), - start_offset, - end_offset, - captures, - true); + ExecuteIA32(*code, + seq_input.location(), + start_offset, + end_offset, + captures, + true); CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result); CHECK_EQ(0, captures[0]); @@ -814,12 +848,12 @@ TEST(MacroAssemblerIA32SimpleUC16) { start_offset = start_adr - reinterpret_cast
(*seq_input); end_offset = start_offset + seq_input->length() * sizeof(uc16); - result = RegExpMacroAssemblerIA32::Execute(*code, - seq_input.location(), - start_offset, - end_offset, - captures, - true); + result = ExecuteIA32(*code, + seq_input.location(), + start_offset, + end_offset, + captures, + true); CHECK_EQ(RegExpMacroAssemblerIA32::FAILURE, result); } @@ -853,12 +887,12 @@ TEST(MacroAssemblerIA32Backtrack) { int end_offset = start_offset + seq_input->length(); RegExpMacroAssemblerIA32::Result result = - RegExpMacroAssemblerIA32::Execute(*code, - seq_input.location(), - start_offset, - end_offset, - NULL, - true); + ExecuteIA32(*code, + seq_input.location(), + start_offset, + end_offset, + NULL, + true); CHECK_EQ(RegExpMacroAssemblerIA32::FAILURE, result); } @@ -897,12 +931,12 @@ TEST(MacroAssemblerIA32BackReferenceASCII) { int output[3]; RegExpMacroAssemblerIA32::Result result = - RegExpMacroAssemblerIA32::Execute(*code, - seq_input.location(), - start_offset, - end_offset, - output, - true); + ExecuteIA32(*code, + seq_input.location(), + start_offset, + end_offset, + output, + true); CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result); CHECK_EQ(0, output[0]); @@ -947,12 +981,12 @@ TEST(MacroAssemblerIA32BackReferenceUC16) { int output[3]; RegExpMacroAssemblerIA32::Result result = - RegExpMacroAssemblerIA32::Execute(*code, - seq_input.location(), - start_offset, - end_offset, - output, - true); + ExecuteIA32(*code, + seq_input.location(), + start_offset, + end_offset, + output, + true); CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result); CHECK_EQ(0, output[0]); @@ -1000,22 +1034,22 @@ TEST(MacroAssemblerIA32AtStart) { int end_offset = start_offset + seq_input->length(); RegExpMacroAssemblerIA32::Result result = - RegExpMacroAssemblerIA32::Execute(*code, - seq_input.location(), - start_offset, - end_offset, - NULL, - true); + ExecuteIA32(*code, + seq_input.location(), + start_offset, + end_offset, + NULL, + true); CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result); start_offset += 3; - result = RegExpMacroAssemblerIA32::Execute(*code, - seq_input.location(), - start_offset, - end_offset, - NULL, - false); + result = ExecuteIA32(*code, + seq_input.location(), + start_offset, + end_offset, + NULL, + false); CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result); } @@ -1065,12 +1099,12 @@ TEST(MacroAssemblerIA32BackRefNoCase) { int output[4]; RegExpMacroAssemblerIA32::Result result = - RegExpMacroAssemblerIA32::Execute(*code, - seq_input.location(), - start_offset, - end_offset, - output, - true); + ExecuteIA32(*code, + seq_input.location(), + start_offset, + end_offset, + output, + true); CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result); CHECK_EQ(0, output[0]); @@ -1094,13 +1128,13 @@ TEST(MacroAssemblerIA32Registers) { Label fail; Label backtrack; m.WriteCurrentPositionToRegister(out1, 0); // Output: [0] - m.PushRegister(out1); + m.PushRegister(out1, RegExpMacroAssembler::kNoStackLimitCheck); m.PushBacktrack(&backtrack); m.WriteStackPointerToRegister(sp); // Fill stack and registers m.AdvanceCurrentPosition(2); m.WriteCurrentPositionToRegister(out1, 0); - m.PushRegister(out1); + m.PushRegister(out1, RegExpMacroAssembler::kNoStackLimitCheck); m.PushBacktrack(&fail); // Drop backtrack stack frames. m.ReadStackPointerFromRegister(sp); @@ -1135,8 +1169,8 @@ TEST(MacroAssemblerIA32Registers) { Label loop3; Label exit_loop3; - m.PushRegister(out4); - m.PushRegister(out4); + m.PushRegister(out4, RegExpMacroAssembler::kNoStackLimitCheck); + m.PushRegister(out4, RegExpMacroAssembler::kNoStackLimitCheck); m.ReadCurrentPositionFromRegister(out3); m.Bind(&loop3); m.AdvanceCurrentPosition(1); @@ -1166,12 +1200,12 @@ TEST(MacroAssemblerIA32Registers) { int output[5]; RegExpMacroAssemblerIA32::Result result = - RegExpMacroAssemblerIA32::Execute(*code, - seq_input.location(), - start_offset, - end_offset, - output, - true); + ExecuteIA32(*code, + seq_input.location(), + start_offset, + end_offset, + output, + true); CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result); CHECK_EQ(0, output[0]); @@ -1207,12 +1241,12 @@ TEST(MacroAssemblerIA32StackOverflow) { int end_offset = start_offset + seq_input->length(); RegExpMacroAssemblerIA32::Result result = - RegExpMacroAssemblerIA32::Execute(*code, - seq_input.location(), - start_offset, - end_offset, - NULL, - true); + ExecuteIA32(*code, + seq_input.location(), + start_offset, + end_offset, + NULL, + true); CHECK_EQ(RegExpMacroAssemblerIA32::EXCEPTION, result); CHECK(Top::has_pending_exception()); @@ -1220,6 +1254,56 @@ TEST(MacroAssemblerIA32StackOverflow) { } +TEST(MacroAssemblerIA32LotsOfRegisters) { + v8::V8::Initialize(); + ContextInitializer initializer; + + RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 2); + + // At least 2048, to ensure the allocated space for registers + // span one full page. + const int large_number = 8000; + m.WriteCurrentPositionToRegister(large_number, 42); + m.WriteCurrentPositionToRegister(0, 0); + m.WriteCurrentPositionToRegister(1, 1); + Label done; + m.CheckNotBackReference(0, &done); // Performs a system-stack push. + m.Bind(&done); + m.PushRegister(large_number, RegExpMacroAssembler::kNoStackLimitCheck); + m.PopRegister(1); + m.Succeed(); + + Handle source = + Factory::NewStringFromAscii(CStrVector("")); + Handle code_object = m.GetCode(source); + Handle code = Handle::cast(code_object); + + // String long enough for test (content doesn't matter). + Handle input = + Factory::NewStringFromAscii(CStrVector("sample text")); + Handle seq_input = Handle::cast(input); + Address start_adr = seq_input->GetCharsAddress(); + int start_offset = start_adr - reinterpret_cast
(*seq_input); + int end_offset = start_offset + seq_input->length(); + + int captures[2]; + RegExpMacroAssemblerIA32::Result result = + ExecuteIA32(*code, + seq_input.location(), + start_offset, + end_offset, + captures, + true); + + CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result); + CHECK_EQ(0, captures[0]); + CHECK_EQ(42, captures[1]); + + Top::clear_pending_exception(); +} + + + #endif // !defined ARM TEST(AddInverseToTable) { diff --git a/tools/visual_studio/v8_base.vcproj b/tools/visual_studio/v8_base.vcproj index 558b0263a..7340fe531 100644 --- a/tools/visual_studio/v8_base.vcproj +++ b/tools/visual_studio/v8_base.vcproj @@ -300,6 +300,10 @@ RelativePath="..\..\src\regexp-macro-assembler-irregexp-inl.h" > + + @@ -744,6 +748,10 @@ RelativePath="..\..\src\regexp-macro-assembler-tracer.h" > + + diff --git a/tools/visual_studio/v8_base_arm.vcproj b/tools/visual_studio/v8_base_arm.vcproj index 7f1adb3e5..3462835ad 100644 --- a/tools/visual_studio/v8_base_arm.vcproj +++ b/tools/visual_studio/v8_base_arm.vcproj @@ -296,6 +296,10 @@ RelativePath="..\..\src\assembler-arm.h" > + + @@ -748,6 +752,10 @@ RelativePath="..\..\src\regexp-macro-assembler-tracer.h" > + +