Index: src/arm/codegen-arm.cc |
=================================================================== |
--- src/arm/codegen-arm.cc (revision 4395) |
+++ src/arm/codegen-arm.cc (working copy) |
@@ -32,7 +32,10 @@ |
#include "compiler.h" |
#include "debug.h" |
#include "ic-inl.h" |
+#include "jsregexp.h" |
#include "parser.h" |
+#include "regexp-macro-assembler.h" |
+#include "regexp-stack.h" |
#include "register-allocator-inl.h" |
#include "runtime.h" |
#include "scopes.h" |
@@ -4015,8 +4018,8 @@ |
Load(args->at(1)); |
Load(args->at(2)); |
Load(args->at(3)); |
- |
- frame_->CallRuntime(Runtime::kRegExpExec, 4); |
+ RegExpExecStub stub; |
+ frame_->CallStub(&stub, 4); |
frame_->EmitPush(r0); |
} |
@@ -7502,6 +7505,345 @@ |
} |
+void RegExpExecStub::Generate(MacroAssembler* masm) { |
+ // Just jump directly to runtime if native RegExp is not selected at compile |
+ // time or if regexp entry in generated code is turned off runtime switch or |
+ // at compilation. |
+#ifndef V8_NATIVE_REGEXP |
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1); |
+#else // V8_NATIVE_REGEXP |
+ if (!FLAG_regexp_entry_native) { |
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1); |
+ return; |
+ } |
+ |
+ // Stack frame on entry. |
+ // sp[0]: last_match_info (expected JSArray) |
+ // sp[4]: previous index |
+ // sp[8]: subject string |
+ // sp[12]: JSRegExp object |
+ |
+ static const int kLastMatchInfoOffset = 0 * kPointerSize; |
+ static const int kPreviousIndexOffset = 1 * kPointerSize; |
+ static const int kSubjectOffset = 2 * kPointerSize; |
+ static const int kJSRegExpOffset = 3 * kPointerSize; |
+ |
+ Label runtime, invoke_regexp; |
+ |
+ // Allocation of registers for this function. These are in callee save |
+ // registers and will be preserved by the call to the native RegExp code, as |
+ // this code is called using the normal C calling convention. When calling |
+ // directly from generated code the native RegExp code will not do a GC and |
+ // therefore the content of these registers are safe to use after the call. |
+ Register subject = r4; |
+ Register regexp_data = r5; |
+ Register last_match_info_elements = r6; |
+ |
+ // Ensure that a RegExp stack is allocated. |
+ ExternalReference address_of_regexp_stack_memory_address = |
+ ExternalReference::address_of_regexp_stack_memory_address(); |
+ ExternalReference address_of_regexp_stack_memory_size = |
+ ExternalReference::address_of_regexp_stack_memory_size(); |
+ __ mov(r0, Operand(address_of_regexp_stack_memory_size)); |
+ __ ldr(r0, MemOperand(r0, 0)); |
+ __ tst(r0, Operand(r0)); |
+ __ b(eq, &runtime); |
+ |
+ // Check that the first argument is a JSRegExp object. |
+ __ ldr(r0, MemOperand(sp, kJSRegExpOffset)); |
+ ASSERT_EQ(0, kSmiTag); |
+ __ tst(r0, Operand(kSmiTagMask)); |
+ __ b(eq, &runtime); |
+ __ CompareObjectType(r0, r1, r1, JS_REGEXP_TYPE); |
+ __ b(ne, &runtime); |
+ |
+ // Check that the RegExp has been compiled (data contains a fixed array). |
+ __ ldr(regexp_data, FieldMemOperand(r0, JSRegExp::kDataOffset)); |
+ if (FLAG_debug_code) { |
+ __ tst(regexp_data, Operand(kSmiTagMask)); |
+ __ Check(nz, "Unexpected type for RegExp data, FixedArray expected"); |
+ __ CompareObjectType(regexp_data, r0, r0, FIXED_ARRAY_TYPE); |
+ __ Check(eq, "Unexpected type for RegExp data, FixedArray expected"); |
+ } |
+ |
+ // regexp_data: RegExp data (FixedArray) |
+ // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP. |
+ __ ldr(r0, FieldMemOperand(regexp_data, JSRegExp::kDataTagOffset)); |
+ __ cmp(r0, Operand(Smi::FromInt(JSRegExp::IRREGEXP))); |
+ __ b(ne, &runtime); |
+ |
+ // regexp_data: RegExp data (FixedArray) |
+ // Check that the number of captures fit in the static offsets vector buffer. |
+ __ ldr(r2, |
+ FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset)); |
+ // Calculate number of capture registers (number_of_captures + 1) * 2. This |
+ // uses the asumption that smis are 2 * their untagged value. |
+ ASSERT_EQ(0, kSmiTag); |
+ ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize); |
+ __ add(r2, r2, Operand(2)); // r2 was a smi. |
+ // Check that the static offsets vector buffer is large enough. |
+ __ cmp(r2, Operand(OffsetsVector::kStaticOffsetsVectorSize)); |
+ __ b(hi, &runtime); |
+ |
+ // r2: Number of capture registers |
+ // regexp_data: RegExp data (FixedArray) |
+ // Check that the second argument is a string. |
+ __ ldr(subject, MemOperand(sp, kSubjectOffset)); |
+ __ tst(subject, Operand(kSmiTagMask)); |
+ __ b(eq, &runtime); |
+ Condition is_string = masm->IsObjectStringType(subject, r0); |
+ __ b(NegateCondition(is_string), &runtime); |
+ // Get the length of the string to r3. |
+ __ ldr(r3, FieldMemOperand(subject, String::kLengthOffset)); |
+ |
+ // r2: Number of capture registers |
+ // r3: Length of subject string |
+ // subject: Subject string |
+ // regexp_data: RegExp data (FixedArray) |
+ // Check that the third argument is a positive smi less than the subject |
+ // string length. A negative value will be greater (unsigned comparison). |
+ __ ldr(r0, MemOperand(sp, kPreviousIndexOffset)); |
+ __ cmp(r3, Operand(r0, ASR, kSmiTagSize + kSmiShiftSize)); |
+ __ b(ls, &runtime); |
+ |
+ // r2: Number of capture registers |
+ // subject: Subject string |
+ // regexp_data: RegExp data (FixedArray) |
+ // Check that the fourth object is a JSArray object. |
+ __ ldr(r0, MemOperand(sp, kLastMatchInfoOffset)); |
+ __ tst(r0, Operand(kSmiTagMask)); |
+ __ b(eq, &runtime); |
+ __ CompareObjectType(r0, r1, r1, JS_ARRAY_TYPE); |
+ __ b(ne, &runtime); |
+ // Check that the JSArray is in fast case. |
+ __ ldr(last_match_info_elements, |
+ FieldMemOperand(r0, JSArray::kElementsOffset)); |
+ __ ldr(r0, FieldMemOperand(last_match_info_elements, HeapObject::kMapOffset)); |
+ __ cmp(r0, Operand(Factory::fixed_array_map())); |
+ __ b(ne, &runtime); |
+ // Check that the last match info has space for the capture registers and the |
+ // additional information. |
+ __ ldr(r0, |
+ FieldMemOperand(last_match_info_elements, FixedArray::kLengthOffset)); |
+ __ add(r2, r2, Operand(RegExpImpl::kLastMatchOverhead)); |
+ __ cmp(r2, r0); |
+ __ b(gt, &runtime); |
+ |
+ // subject: Subject string |
+ // regexp_data: RegExp data (FixedArray) |
+ // Check the representation and encoding of the subject string. |
+ Label seq_string; |
+ const int kStringRepresentationEncodingMask = |
+ kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask; |
+ __ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset)); |
+ __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset)); |
+ __ and_(r1, r0, Operand(kStringRepresentationEncodingMask)); |
+ // First check for sequential string. |
+ ASSERT_EQ(0, kStringTag); |
+ ASSERT_EQ(0, kSeqStringTag); |
+ __ tst(r1, Operand(kIsNotStringMask | kStringRepresentationMask)); |
+ __ b(eq, &seq_string); |
+ |
+ // subject: Subject string |
+ // regexp_data: RegExp data (FixedArray) |
+ // Check for flat cons string. |
+ // A flat cons string is a cons string where the second part is the empty |
+ // string. In that case the subject string is just the first part of the cons |
+ // string. Also in this case the first part of the cons string is known to be |
+ // a sequential string or an external string. |
+ __ and_(r0, r0, Operand(kStringRepresentationMask)); |
+ __ cmp(r0, Operand(kConsStringTag)); |
+ __ b(ne, &runtime); |
+ __ ldr(r0, FieldMemOperand(subject, ConsString::kSecondOffset)); |
+ __ LoadRoot(r1, Heap::kEmptyStringRootIndex); |
+ __ cmp(r0, r1); |
+ __ b(ne, &runtime); |
+ __ ldr(subject, FieldMemOperand(subject, ConsString::kFirstOffset)); |
+ __ ldr(r0, FieldMemOperand(subject, HeapObject::kMapOffset)); |
+ __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset)); |
+ ASSERT_EQ(0, kSeqStringTag); |
+ __ tst(r0, Operand(kStringRepresentationMask)); |
+ __ b(nz, &runtime); |
+ __ and_(r1, r0, Operand(kStringRepresentationEncodingMask)); |
+ |
+ __ bind(&seq_string); |
+ // r1: suject string type & kStringRepresentationEncodingMask |
+ // subject: Subject string |
+ // regexp_data: RegExp data (FixedArray) |
+ // Check that the irregexp code has been generated for an ascii string. If |
+ // it has, the field contains a code object otherwise it contains the hole. |
+#ifdef DEBUG |
+ const int kSeqAsciiString = kStringTag | kSeqStringTag | kAsciiStringTag; |
+ const int kSeqTwoByteString = kStringTag | kSeqStringTag | kTwoByteStringTag; |
+ CHECK_EQ(4, kSeqAsciiString); |
+ CHECK_EQ(0, kSeqTwoByteString); |
+#endif |
+ // Find the code object based on the assumptions above. |
+ __ mov(r3, Operand(r1, ASR, 2), SetCC); |
+ __ ldr(r7, FieldMemOperand(regexp_data, JSRegExp::kDataAsciiCodeOffset), ne); |
+ __ ldr(r7, FieldMemOperand(regexp_data, JSRegExp::kDataUC16CodeOffset), eq); |
+ |
+ // Check that the irregexp code has been generated for the actual string |
+ // encoding. If it has, the field contains a code object otherwise it contains |
+ // the hole. |
+ __ CompareObjectType(r7, r0, r0, CODE_TYPE); |
+ __ b(ne, &runtime); |
+ |
+ // r3: encoding of subject string (1 if ascii, 0 if two_byte); |
+ // r7: code |
+ // subject: Subject string |
+ // regexp_data: RegExp data (FixedArray) |
+ // Load used arguments before starting to push arguments for call to native |
+ // RegExp code to avoid handling changing stack height. |
+ __ ldr(r1, MemOperand(sp, kPreviousIndexOffset)); |
+ __ mov(r1, Operand(r1, ASR, kSmiTagSize)); |
+ |
+ // r1: previous index |
+ // r3: encoding of subject string (1 if ascii, 0 if two_byte); |
+ // r7: code |
+ // subject: Subject string |
+ // regexp_data: RegExp data (FixedArray) |
+ // All checks done. Now push arguments for native regexp code. |
+ __ IncrementCounter(&Counters::regexp_entry_native, 1, r0, r2); |
+ |
+ static const int kRegExpExecuteArguments = 7; |
+ __ push(lr); |
+ __ PrepareCallCFunction(kRegExpExecuteArguments, r0); |
+ |
+ // Argument 7 (sp[8]): Indicate that this is a direct call from JavaScript. |
+ __ mov(r0, Operand(1)); |
+ __ str(r0, MemOperand(sp, 2 * kPointerSize)); |
+ |
+ // Argument 6 (sp[4]): 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)); |
+ |
+ // Argument 5 (sp[0]): static offsets vector buffer. |
+ __ mov(r0, Operand(ExternalReference::address_of_static_offsets_vector())); |
+ __ str(r0, MemOperand(sp, 0 * 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). |
+ __ ldr(r0, FieldMemOperand(subject, String::kLengthOffset)); |
+ ASSERT_EQ(SeqAsciiString::kHeaderSize, SeqTwoByteString::kHeaderSize); |
+ __ add(r9, subject, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
+ __ eor(r3, r3, Operand(1)); |
+ // Argument 4 (r3): End of string data |
+ // Argument 3 (r2): Start of string data |
+ __ add(r2, r9, Operand(r1, LSL, r3)); |
+ __ add(r3, r9, Operand(r0, LSL, r3)); |
+ |
+ // Argument 2 (r1): Previous index. |
+ // Already there |
+ |
+ // Argument 1 (r0): Subject string. |
+ __ mov(r0, subject); |
+ |
+ // Locate the code entry and call it. |
+ __ add(r7, r7, Operand(Code::kHeaderSize - kHeapObjectTag)); |
+ __ CallCFunction(r7, kRegExpExecuteArguments); |
+ __ pop(lr); |
+ |
+ // r0: result |
+ // subject: subject string (callee saved) |
+ // regexp_data: RegExp data (callee saved) |
+ // last_match_info_elements: Last match info elements (callee saved) |
+ |
+ // Check the result. |
+ Label success; |
+ __ cmp(r0, Operand(NativeRegExpMacroAssembler::SUCCESS)); |
+ __ b(eq, &success); |
+ Label failure; |
+ __ cmp(r0, Operand(NativeRegExpMacroAssembler::FAILURE)); |
+ __ b(eq, &failure); |
+ __ cmp(r0, Operand(NativeRegExpMacroAssembler::EXCEPTION)); |
+ // If not exception it can only be retry. Handle that in the runtime system. |
+ __ b(ne, &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. |
+ __ mov(r0, Operand(ExternalReference::the_hole_value_location())); |
+ __ ldr(r0, MemOperand(r0, 0)); |
+ __ mov(r1, Operand(ExternalReference(Top::k_pending_exception_address))); |
+ __ ldr(r1, MemOperand(r1, 0)); |
+ __ cmp(r0, r1); |
+ __ b(eq, &runtime); |
+ __ bind(&failure); |
+ // For failure and exception return null. |
+ __ mov(r0, Operand(Factory::null_value())); |
+ __ add(sp, sp, Operand(4 * kPointerSize)); |
+ __ Ret(); |
+ |
+ // Process the result from the native regexp code. |
+ __ bind(&success); |
+ __ ldr(r1, |
+ FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset)); |
+ // Calculate number of capture registers (number_of_captures + 1) * 2. |
+ ASSERT_EQ(0, kSmiTag); |
+ ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize); |
+ __ add(r1, r1, Operand(2)); // r1 was a smi. |
+ |
+ // r1: number of capture registers |
+ // r4: subject string |
+ // Store the capture count. |
+ __ mov(r2, Operand(r1, LSL, kSmiTagSize + kSmiShiftSize)); // To smi. |
+ __ str(r2, FieldMemOperand(last_match_info_elements, |
+ RegExpImpl::kLastCaptureCountOffset)); |
+ // Store last subject and last input. |
+ __ mov(r3, last_match_info_elements); // Moved up to reduce latency. |
+ __ mov(r2, Operand(RegExpImpl::kLastSubjectOffset)); // Ditto. |
+ __ str(subject, |
+ FieldMemOperand(last_match_info_elements, |
+ RegExpImpl::kLastSubjectOffset)); |
+ __ RecordWrite(r3, r2, r7); |
+ __ str(subject, |
+ FieldMemOperand(last_match_info_elements, |
+ RegExpImpl::kLastInputOffset)); |
+ __ mov(r3, last_match_info_elements); |
+ __ mov(r2, Operand(RegExpImpl::kLastInputOffset)); |
+ __ RecordWrite(r3, r2, r7); |
+ |
+ // Get the static offsets vector filled by the native regexp code. |
+ ExternalReference address_of_static_offsets_vector = |
+ ExternalReference::address_of_static_offsets_vector(); |
+ __ mov(r2, Operand(address_of_static_offsets_vector)); |
+ |
+ // r1: number of capture registers |
+ // r2: offsets vector |
+ Label next_capture, done; |
+ // Capture register counter starts from number of capture registers and |
+ // counts down until wraping after zero. |
+ __ add(r0, |
+ last_match_info_elements, |
+ Operand(RegExpImpl::kFirstCaptureOffset - kHeapObjectTag)); |
+ __ bind(&next_capture); |
+ __ sub(r1, r1, Operand(1), SetCC); |
+ __ b(mi, &done); |
+ // Read the value from the static offsets vector buffer. |
+ __ ldr(r3, MemOperand(r2, kPointerSize, PostIndex)); |
+ // Store the smi value in the last match info. |
+ __ mov(r3, Operand(r3, LSL, kSmiTagSize)); |
+ __ str(r3, MemOperand(r0, kPointerSize, PostIndex)); |
+ __ jmp(&next_capture); |
+ __ bind(&done); |
+ |
+ // Return last match info. |
+ __ ldr(r0, MemOperand(sp, kLastMatchInfoOffset)); |
+ __ add(sp, sp, Operand(4 * kPointerSize)); |
+ __ Ret(); |
+ |
+ // Do the runtime call to execute the regexp. |
+ __ bind(&runtime); |
+ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1); |
+#endif // V8_NATIVE_REGEXP |
+} |
+ |
+ |
void CallFunctionStub::Generate(MacroAssembler* masm) { |
Label slow; |