OLD | NEW |
1 // Copyright 2017 the V8 project authors. All rights reserved. | 1 // Copyright 2017 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/builtins/builtins-regexp-gen.h" | 5 #include "src/builtins/builtins-regexp-gen.h" |
6 | 6 |
7 #include "src/builtins/builtins-constructor-gen.h" | 7 #include "src/builtins/builtins-constructor-gen.h" |
8 #include "src/builtins/builtins-utils-gen.h" | 8 #include "src/builtins/builtins-utils-gen.h" |
9 #include "src/builtins/builtins.h" | 9 #include "src/builtins/builtins.h" |
10 #include "src/code-factory.h" | 10 #include "src/code-factory.h" |
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
224 | 224 |
225 Node* const from_offset = ElementOffsetFromIndex( | 225 Node* const from_offset = ElementOffsetFromIndex( |
226 IntPtrAdd(offset, last_index), kind, INTPTR_PARAMETERS); | 226 IntPtrAdd(offset, last_index), kind, INTPTR_PARAMETERS); |
227 var_string_start->Bind(IntPtrAdd(string_data, from_offset)); | 227 var_string_start->Bind(IntPtrAdd(string_data, from_offset)); |
228 | 228 |
229 Node* const to_offset = ElementOffsetFromIndex( | 229 Node* const to_offset = ElementOffsetFromIndex( |
230 IntPtrAdd(offset, string_length), kind, INTPTR_PARAMETERS); | 230 IntPtrAdd(offset, string_length), kind, INTPTR_PARAMETERS); |
231 var_string_end->Bind(IntPtrAdd(string_data, to_offset)); | 231 var_string_end->Bind(IntPtrAdd(string_data, to_offset)); |
232 } | 232 } |
233 | 233 |
234 Node* RegExpBuiltinsAssembler::RegExpExecInternal(Node* const context, | 234 Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context, |
235 Node* const regexp, | 235 Node* const regexp, |
236 Node* const string, | 236 Node* const string, |
237 Node* const last_index, | 237 Node* const last_index, |
238 Node* const match_info) { | 238 Node* const match_info) { |
239 // Just jump directly to runtime if native RegExp is not selected at compile | 239 // Just jump directly to runtime if native RegExp is not selected at compile |
240 // time or if regexp entry in generated code is turned off runtime switch or | 240 // time or if regexp entry in generated code is turned off runtime switch or |
241 // at compilation. | 241 // at compilation. |
242 #ifdef V8_INTERPRETED_REGEXP | 242 #ifdef V8_INTERPRETED_REGEXP |
243 return CallRuntime(Runtime::kRegExpExec, context, regexp, string, last_index, | 243 return CallRuntime(Runtime::kRegExpExec, context, regexp, string, last_index, |
244 match_info); | 244 match_info); |
245 #else // V8_INTERPRETED_REGEXP | 245 #else // V8_INTERPRETED_REGEXP |
246 CSA_ASSERT(this, TaggedIsNotSmi(regexp)); | 246 CSA_ASSERT(this, TaggedIsNotSmi(regexp)); |
247 CSA_ASSERT(this, IsJSRegExp(regexp)); | 247 CSA_ASSERT(this, IsJSRegExp(regexp)); |
248 | 248 |
249 CSA_ASSERT(this, TaggedIsNotSmi(string)); | 249 CSA_ASSERT(this, TaggedIsNotSmi(string)); |
250 CSA_ASSERT(this, IsString(string)); | 250 CSA_ASSERT(this, IsString(string)); |
251 | 251 |
252 CSA_ASSERT(this, IsNumber(last_index)); | 252 CSA_ASSERT(this, IsNumber(last_index)); |
253 CSA_ASSERT(this, IsFixedArrayMap(LoadReceiverMap(match_info))); | 253 CSA_ASSERT(this, IsFixedArrayMap(LoadReceiverMap(match_info))); |
254 | 254 |
255 Node* const int_zero = IntPtrConstant(0); | 255 Node* const int_zero = IntPtrConstant(0); |
256 | 256 |
257 ToDirectStringAssembler to_direct(state(), string); | 257 ToDirectStringAssembler to_direct(state(), string); |
258 | 258 |
259 VARIABLE(var_result, MachineRepresentation::kTagged); | 259 VARIABLE(var_result, MachineRepresentation::kTagged); |
260 Label out(this), runtime(this, Label::kDeferred); | 260 Label out(this), runtime(this, Label::kDeferred); |
261 | 261 |
262 // External constants. | 262 // External constants. |
263 Node* const isolate_address = | |
264 ExternalConstant(ExternalReference::isolate_address(isolate())); | |
265 Node* const regexp_stack_memory_address_address = ExternalConstant( | |
266 ExternalReference::address_of_regexp_stack_memory_address(isolate())); | |
267 Node* const regexp_stack_memory_size_address = ExternalConstant( | 263 Node* const regexp_stack_memory_size_address = ExternalConstant( |
268 ExternalReference::address_of_regexp_stack_memory_size(isolate())); | 264 ExternalReference::address_of_regexp_stack_memory_size(isolate())); |
269 Node* const static_offsets_vector_address = ExternalConstant( | 265 Node* const static_offsets_vector_address = ExternalConstant( |
270 ExternalReference::address_of_static_offsets_vector(isolate())); | 266 ExternalReference::address_of_static_offsets_vector(isolate())); |
| 267 Node* const pending_exception_address = ExternalConstant( |
| 268 ExternalReference(Isolate::kPendingExceptionAddress, isolate())); |
271 | 269 |
272 // Ensure that a RegExp stack is allocated. | 270 // Ensure that a RegExp stack is allocated. |
273 { | 271 { |
274 Node* const stack_size = | 272 Node* const stack_size = |
275 Load(MachineType::IntPtr(), regexp_stack_memory_size_address); | 273 Load(MachineType::IntPtr(), regexp_stack_memory_size_address); |
276 GotoIf(IntPtrEqual(stack_size, int_zero), &runtime); | 274 GotoIf(IntPtrEqual(stack_size, int_zero), &runtime); |
277 } | 275 } |
278 | 276 |
279 Node* const data = LoadObjectField(regexp, JSRegExp::kDataOffset); | 277 Node* const data = LoadObjectField(regexp, JSRegExp::kDataOffset); |
280 { | 278 { |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
356 | 354 |
357 Node* const code = var_code.value(); | 355 Node* const code = var_code.value(); |
358 GotoIf(TaggedIsSmi(code), &runtime); | 356 GotoIf(TaggedIsSmi(code), &runtime); |
359 CSA_ASSERT(this, HasInstanceType(code, CODE_TYPE)); | 357 CSA_ASSERT(this, HasInstanceType(code, CODE_TYPE)); |
360 | 358 |
361 Label if_success(this), if_failure(this), | 359 Label if_success(this), if_failure(this), |
362 if_exception(this, Label::kDeferred); | 360 if_exception(this, Label::kDeferred); |
363 { | 361 { |
364 IncrementCounter(isolate()->counters()->regexp_entry_native(), 1); | 362 IncrementCounter(isolate()->counters()->regexp_entry_native(), 1); |
365 | 363 |
366 // Set up args for the final call into generated Irregexp code. | 364 Callable exec_callable = CodeFactory::RegExpExec(isolate()); |
367 | 365 Node* const result = CallStub( |
368 MachineType type_int32 = MachineType::Int32(); | 366 exec_callable, context, string, TruncateWordToWord32(int_last_index), |
369 MachineType type_tagged = MachineType::AnyTagged(); | 367 var_string_start.value(), var_string_end.value(), code); |
370 MachineType type_ptr = MachineType::Pointer(); | |
371 | |
372 // Result: A NativeRegExpMacroAssembler::Result return code. | |
373 MachineType retval_type = type_int32; | |
374 | |
375 // Argument 0: Original subject string. | |
376 MachineType arg0_type = type_tagged; | |
377 Node* const arg0 = string; | |
378 | |
379 // Argument 1: Previous index. | |
380 MachineType arg1_type = type_int32; | |
381 Node* const arg1 = TruncateWordToWord32(int_last_index); | |
382 | |
383 // Argument 2: Start of string data. | |
384 MachineType arg2_type = type_ptr; | |
385 Node* const arg2 = var_string_start.value(); | |
386 | |
387 // Argument 3: End of string data. | |
388 MachineType arg3_type = type_ptr; | |
389 Node* const arg3 = var_string_end.value(); | |
390 | |
391 // Argument 4: static offsets vector buffer. | |
392 MachineType arg4_type = type_ptr; | |
393 Node* const arg4 = static_offsets_vector_address; | |
394 | |
395 // Argument 5: Set the number of capture registers to zero to force global | |
396 // regexps to behave as non-global. This does not affect non-global | |
397 // regexps. | |
398 MachineType arg5_type = type_int32; | |
399 Node* const arg5 = Int32Constant(0); | |
400 | |
401 // Argument 6: Start (high end) of backtracking stack memory area. | |
402 Node* const stack_start = | |
403 Load(MachineType::Pointer(), regexp_stack_memory_address_address); | |
404 Node* const stack_size = | |
405 Load(MachineType::IntPtr(), regexp_stack_memory_size_address); | |
406 Node* const stack_end = IntPtrAdd(stack_start, stack_size); | |
407 | |
408 MachineType arg6_type = type_ptr; | |
409 Node* const arg6 = stack_end; | |
410 | |
411 // Argument 7: Indicate that this is a direct call from JavaScript. | |
412 MachineType arg7_type = type_int32; | |
413 Node* const arg7 = Int32Constant(1); | |
414 | |
415 // Argument 8: Pass current isolate address. | |
416 MachineType arg8_type = type_ptr; | |
417 Node* const arg8 = isolate_address; | |
418 | |
419 Node* const code_entry = | |
420 IntPtrAdd(BitcastTaggedToWord(code), | |
421 IntPtrConstant(Code::kHeaderSize - kHeapObjectTag)); | |
422 | |
423 Node* const result = CallCFunction9( | |
424 retval_type, arg0_type, arg1_type, arg2_type, arg3_type, arg4_type, | |
425 arg5_type, arg6_type, arg7_type, arg8_type, code_entry, arg0, arg1, | |
426 arg2, arg3, arg4, arg5, arg6, arg7, arg8); | |
427 | 368 |
428 // Check the result. | 369 // Check the result. |
429 // We expect exactly one result since we force the called regexp to behave | 370 // We expect exactly one result since the stub forces the called regexp to |
430 // as non-global. | 371 // behave as non-global. |
431 Node* const int_result = ChangeInt32ToIntPtr(result); | 372 GotoIf(SmiEqual(result, SmiConstant(1)), &if_success); |
432 GotoIf(IntPtrEqual(int_result, | 373 GotoIf(SmiEqual(result, SmiConstant(NativeRegExpMacroAssembler::FAILURE)), |
433 IntPtrConstant(NativeRegExpMacroAssembler::SUCCESS)), | |
434 &if_success); | |
435 GotoIf(IntPtrEqual(int_result, | |
436 IntPtrConstant(NativeRegExpMacroAssembler::FAILURE)), | |
437 &if_failure); | 374 &if_failure); |
438 GotoIf(IntPtrEqual(int_result, | 375 GotoIf(SmiEqual(result, SmiConstant(NativeRegExpMacroAssembler::EXCEPTION)), |
439 IntPtrConstant(NativeRegExpMacroAssembler::EXCEPTION)), | |
440 &if_exception); | 376 &if_exception); |
441 | 377 |
442 CSA_ASSERT(this, | 378 CSA_ASSERT( |
443 IntPtrEqual(int_result, | 379 this, SmiEqual(result, SmiConstant(NativeRegExpMacroAssembler::RETRY))); |
444 IntPtrConstant(NativeRegExpMacroAssembler::RETRY))); | |
445 Goto(&runtime); | 380 Goto(&runtime); |
446 } | 381 } |
447 | 382 |
448 BIND(&if_success); | 383 BIND(&if_success); |
449 { | 384 { |
450 // Check that the last match info has space for the capture registers and | 385 // Check that the last match info has space for the capture registers and |
451 // the additional information. Ensure no overflow in add. | 386 // the additional information. Ensure no overflow in add. |
452 STATIC_ASSERT(FixedArray::kMaxLength < kMaxInt - FixedArray::kLengthOffset); | 387 STATIC_ASSERT(FixedArray::kMaxLength < kMaxInt - FixedArray::kLengthOffset); |
453 Node* const available_slots = | 388 Node* const available_slots = |
454 SmiSub(LoadFixedArrayBaseLength(match_info), | 389 SmiSub(LoadFixedArrayBaseLength(match_info), |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
498 } | 433 } |
499 | 434 |
500 BIND(&if_failure); | 435 BIND(&if_failure); |
501 { | 436 { |
502 var_result.Bind(NullConstant()); | 437 var_result.Bind(NullConstant()); |
503 Goto(&out); | 438 Goto(&out); |
504 } | 439 } |
505 | 440 |
506 BIND(&if_exception); | 441 BIND(&if_exception); |
507 { | 442 { |
508 // A stack overflow was detected in RegExp code. | 443 Node* const pending_exception = |
509 #ifdef DEBUG | 444 Load(MachineType::AnyTagged(), pending_exception_address); |
510 Node* const pending_exception_address = ExternalConstant( | 445 |
511 ExternalReference(Isolate::kPendingExceptionAddress, isolate())); | 446 // If there is no pending exception, a |
512 CSA_ASSERT(this, IsTheHole(Load(MachineType::AnyTagged(), | 447 // stack overflow (on the backtrack stack) was detected in RegExp code. |
513 pending_exception_address))); | 448 |
514 #endif // DEBUG | 449 Label stack_overflow(this), rethrow(this); |
| 450 Branch(IsTheHole(pending_exception), &stack_overflow, &rethrow); |
| 451 |
| 452 BIND(&stack_overflow); |
515 CallRuntime(Runtime::kThrowStackOverflow, context); | 453 CallRuntime(Runtime::kThrowStackOverflow, context); |
516 Unreachable(); | 454 Unreachable(); |
| 455 |
| 456 BIND(&rethrow); |
| 457 CallRuntime(Runtime::kRegExpExecReThrow, context); |
| 458 Unreachable(); |
517 } | 459 } |
518 | 460 |
519 BIND(&runtime); | 461 BIND(&runtime); |
520 { | 462 { |
521 Node* const result = CallRuntime(Runtime::kRegExpExec, context, regexp, | 463 Node* const result = CallRuntime(Runtime::kRegExpExec, context, regexp, |
522 string, last_index, match_info); | 464 string, last_index, match_info); |
523 var_result.Bind(result); | 465 var_result.Bind(result); |
524 Goto(&out); | 466 Goto(&out); |
525 } | 467 } |
526 | 468 |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
622 Node* match_indices; | 564 Node* match_indices; |
623 Label successful_match(this); | 565 Label successful_match(this); |
624 BIND(&run_exec); | 566 BIND(&run_exec); |
625 { | 567 { |
626 // Get last match info from the context. | 568 // Get last match info from the context. |
627 Node* const native_context = LoadNativeContext(context); | 569 Node* const native_context = LoadNativeContext(context); |
628 Node* const last_match_info = LoadContextElement( | 570 Node* const last_match_info = LoadContextElement( |
629 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 571 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
630 | 572 |
631 // Call the exec stub. | 573 // Call the exec stub. |
632 match_indices = RegExpExecInternal(context, regexp, string, | 574 match_indices = IrregexpExec(context, regexp, string, var_lastindex.value(), |
633 var_lastindex.value(), last_match_info); | 575 last_match_info); |
634 var_result.Bind(match_indices); | 576 var_result.Bind(match_indices); |
635 | 577 |
636 // {match_indices} is either null or the RegExpMatchInfo array. | 578 // {match_indices} is either null or the RegExpMatchInfo array. |
637 // Return early if exec failed, possibly updating last index. | 579 // Return early if exec failed, possibly updating last index. |
638 GotoIfNot(WordEqual(match_indices, null), &successful_match); | 580 GotoIfNot(WordEqual(match_indices, null), &successful_match); |
639 | 581 |
640 GotoIfNot(should_update_last_index, if_didnotmatch); | 582 GotoIfNot(should_update_last_index, if_didnotmatch); |
641 | 583 |
642 StoreLastIndex(context, regexp, smi_zero, is_fastpath); | 584 StoreLastIndex(context, regexp, smi_zero, is_fastpath); |
643 Goto(if_didnotmatch); | 585 Goto(if_didnotmatch); |
(...skipping 1511 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2155 // array depending on whether the {regexp} matches. | 2097 // array depending on whether the {regexp} matches. |
2156 { | 2098 { |
2157 Label next(this), if_stringisempty(this, Label::kDeferred); | 2099 Label next(this), if_stringisempty(this, Label::kDeferred); |
2158 Branch(SmiEqual(string_length, smi_zero), &if_stringisempty, &next); | 2100 Branch(SmiEqual(string_length, smi_zero), &if_stringisempty, &next); |
2159 | 2101 |
2160 BIND(&if_stringisempty); | 2102 BIND(&if_stringisempty); |
2161 { | 2103 { |
2162 Node* const last_match_info = LoadContextElement( | 2104 Node* const last_match_info = LoadContextElement( |
2163 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 2105 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
2164 | 2106 |
2165 Node* const match_indices = RegExpExecInternal(context, regexp, string, | 2107 Node* const match_indices = |
2166 smi_zero, last_match_info); | 2108 IrregexpExec(context, regexp, string, smi_zero, last_match_info); |
2167 | 2109 |
2168 Label return_singleton_array(this); | 2110 Label return_singleton_array(this); |
2169 Branch(WordEqual(match_indices, null), &return_singleton_array, | 2111 Branch(WordEqual(match_indices, null), &return_singleton_array, |
2170 &return_empty_array); | 2112 &return_empty_array); |
2171 | 2113 |
2172 BIND(&return_singleton_array); | 2114 BIND(&return_singleton_array); |
2173 { | 2115 { |
2174 Node* const length = SmiConstant(1); | 2116 Node* const length = SmiConstant(1); |
2175 Node* const capacity = IntPtrConstant(1); | 2117 Node* const capacity = IntPtrConstant(1); |
2176 Node* const result = AllocateJSArray(kind, array_map, capacity, length, | 2118 Node* const result = AllocateJSArray(kind, array_map, capacity, length, |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2217 Branch(SmiEqual(next_search_from, string_length), &push_suffix_and_out, | 2159 Branch(SmiEqual(next_search_from, string_length), &push_suffix_and_out, |
2218 &next); | 2160 &next); |
2219 BIND(&next); | 2161 BIND(&next); |
2220 } | 2162 } |
2221 | 2163 |
2222 // Search for the given {regexp}. | 2164 // Search for the given {regexp}. |
2223 | 2165 |
2224 Node* const last_match_info = LoadContextElement( | 2166 Node* const last_match_info = LoadContextElement( |
2225 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 2167 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
2226 | 2168 |
2227 Node* const match_indices = RegExpExecInternal( | 2169 Node* const match_indices = IrregexpExec(context, regexp, string, |
2228 context, regexp, string, next_search_from, last_match_info); | 2170 next_search_from, last_match_info); |
2229 | 2171 |
2230 // We're done if no match was found. | 2172 // We're done if no match was found. |
2231 { | 2173 { |
2232 Label next(this); | 2174 Label next(this); |
2233 Branch(WordEqual(match_indices, null), &push_suffix_and_out, &next); | 2175 Branch(WordEqual(match_indices, null), &push_suffix_and_out, &next); |
2234 BIND(&next); | 2176 BIND(&next); |
2235 } | 2177 } |
2236 | 2178 |
2237 Node* const match_from = LoadFixedArrayElement( | 2179 Node* const match_from = LoadFixedArrayElement( |
2238 match_indices, RegExpMatchInfo::kFirstCaptureIndex); | 2180 match_indices, RegExpMatchInfo::kFirstCaptureIndex); |
(...skipping 660 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2899 Node* const null = NullConstant(); | 2841 Node* const null = NullConstant(); |
2900 Node* const smi_zero = SmiConstant(0); | 2842 Node* const smi_zero = SmiConstant(0); |
2901 | 2843 |
2902 CSA_ASSERT(this, IsJSRegExp(regexp)); | 2844 CSA_ASSERT(this, IsJSRegExp(regexp)); |
2903 CSA_ASSERT(this, IsString(string)); | 2845 CSA_ASSERT(this, IsString(string)); |
2904 | 2846 |
2905 Node* const native_context = LoadNativeContext(context); | 2847 Node* const native_context = LoadNativeContext(context); |
2906 Node* const internal_match_info = LoadContextElement( | 2848 Node* const internal_match_info = LoadContextElement( |
2907 native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX); | 2849 native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX); |
2908 | 2850 |
2909 Node* const match_indices = RegExpExecInternal(context, regexp, string, | 2851 Node* const match_indices = |
2910 smi_zero, internal_match_info); | 2852 IrregexpExec(context, regexp, string, smi_zero, internal_match_info); |
2911 | 2853 |
2912 Label if_matched(this), if_didnotmatch(this); | 2854 Label if_matched(this), if_didnotmatch(this); |
2913 Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); | 2855 Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); |
2914 | 2856 |
2915 BIND(&if_didnotmatch); | 2857 BIND(&if_didnotmatch); |
2916 Return(null); | 2858 Return(null); |
2917 | 2859 |
2918 BIND(&if_matched); | 2860 BIND(&if_matched); |
2919 { | 2861 { |
2920 Node* result = | 2862 Node* result = |
2921 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); | 2863 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); |
2922 Return(result); | 2864 Return(result); |
2923 } | 2865 } |
2924 } | 2866 } |
2925 | 2867 |
2926 } // namespace internal | 2868 } // namespace internal |
2927 } // namespace v8 | 2869 } // namespace v8 |
OLD | NEW |