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::IrregexpExec(Node* const context, | 234 Node* RegExpBuiltinsAssembler::RegExpExecInternal(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())); |
263 Node* const regexp_stack_memory_size_address = ExternalConstant( | 267 Node* const regexp_stack_memory_size_address = ExternalConstant( |
264 ExternalReference::address_of_regexp_stack_memory_size(isolate())); | 268 ExternalReference::address_of_regexp_stack_memory_size(isolate())); |
265 Node* const static_offsets_vector_address = ExternalConstant( | 269 Node* const static_offsets_vector_address = ExternalConstant( |
266 ExternalReference::address_of_static_offsets_vector(isolate())); | 270 ExternalReference::address_of_static_offsets_vector(isolate())); |
267 Node* const pending_exception_address = ExternalConstant( | |
268 ExternalReference(Isolate::kPendingExceptionAddress, isolate())); | |
269 | 271 |
270 // Ensure that a RegExp stack is allocated. | 272 // Ensure that a RegExp stack is allocated. |
271 { | 273 { |
272 Node* const stack_size = | 274 Node* const stack_size = |
273 Load(MachineType::IntPtr(), regexp_stack_memory_size_address); | 275 Load(MachineType::IntPtr(), regexp_stack_memory_size_address); |
274 GotoIf(IntPtrEqual(stack_size, int_zero), &runtime); | 276 GotoIf(IntPtrEqual(stack_size, int_zero), &runtime); |
275 } | 277 } |
276 | 278 |
277 Node* const data = LoadObjectField(regexp, JSRegExp::kDataOffset); | 279 Node* const data = LoadObjectField(regexp, JSRegExp::kDataOffset); |
278 { | 280 { |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
354 | 356 |
355 Node* const code = var_code.value(); | 357 Node* const code = var_code.value(); |
356 GotoIf(TaggedIsSmi(code), &runtime); | 358 GotoIf(TaggedIsSmi(code), &runtime); |
357 CSA_ASSERT(this, HasInstanceType(code, CODE_TYPE)); | 359 CSA_ASSERT(this, HasInstanceType(code, CODE_TYPE)); |
358 | 360 |
359 Label if_success(this), if_failure(this), | 361 Label if_success(this), if_failure(this), |
360 if_exception(this, Label::kDeferred); | 362 if_exception(this, Label::kDeferred); |
361 { | 363 { |
362 IncrementCounter(isolate()->counters()->regexp_entry_native(), 1); | 364 IncrementCounter(isolate()->counters()->regexp_entry_native(), 1); |
363 | 365 |
364 Callable exec_callable = CodeFactory::RegExpExec(isolate()); | 366 // Set up args for the final call into generated Irregexp code. |
365 Node* const result = CallStub( | 367 |
366 exec_callable, context, string, TruncateWordToWord32(int_last_index), | 368 MachineType type_int32 = MachineType::Int32(); |
367 var_string_start.value(), var_string_end.value(), code); | 369 MachineType type_tagged = MachineType::AnyTagged(); |
| 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 // Argument 3: End of string data. |
| 385 MachineType arg2_type = type_ptr; |
| 386 Node* const arg2 = var_string_start.value(); |
| 387 |
| 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); |
368 | 427 |
369 // Check the result. | 428 // Check the result. |
370 // We expect exactly one result since the stub forces the called regexp to | 429 // We expect exactly one result since we force the called regexp to behave |
371 // behave as non-global. | 430 // as non-global. |
372 GotoIf(SmiEqual(result, SmiConstant(1)), &if_success); | 431 Node* const int_result = ChangeInt32ToIntPtr(result); |
373 GotoIf(SmiEqual(result, SmiConstant(NativeRegExpMacroAssembler::FAILURE)), | 432 GotoIf(IntPtrEqual(int_result, IntPtrConstant(1)), &if_success); |
| 433 GotoIf(IntPtrEqual(int_result, |
| 434 IntPtrConstant(NativeRegExpMacroAssembler::FAILURE)), |
374 &if_failure); | 435 &if_failure); |
375 GotoIf(SmiEqual(result, SmiConstant(NativeRegExpMacroAssembler::EXCEPTION)), | 436 GotoIf(IntPtrEqual(int_result, |
| 437 IntPtrConstant(NativeRegExpMacroAssembler::EXCEPTION)), |
376 &if_exception); | 438 &if_exception); |
377 | 439 |
378 CSA_ASSERT( | 440 CSA_ASSERT(this, |
379 this, SmiEqual(result, SmiConstant(NativeRegExpMacroAssembler::RETRY))); | 441 IntPtrEqual(int_result, |
| 442 IntPtrConstant(NativeRegExpMacroAssembler::RETRY))); |
380 Goto(&runtime); | 443 Goto(&runtime); |
381 } | 444 } |
382 | 445 |
383 BIND(&if_success); | 446 BIND(&if_success); |
384 { | 447 { |
385 // Check that the last match info has space for the capture registers and | 448 // Check that the last match info has space for the capture registers and |
386 // the additional information. Ensure no overflow in add. | 449 // the additional information. Ensure no overflow in add. |
387 STATIC_ASSERT(FixedArray::kMaxLength < kMaxInt - FixedArray::kLengthOffset); | 450 STATIC_ASSERT(FixedArray::kMaxLength < kMaxInt - FixedArray::kLengthOffset); |
388 Node* const available_slots = | 451 Node* const available_slots = |
389 SmiSub(LoadFixedArrayBaseLength(match_info), | 452 SmiSub(LoadFixedArrayBaseLength(match_info), |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
433 } | 496 } |
434 | 497 |
435 BIND(&if_failure); | 498 BIND(&if_failure); |
436 { | 499 { |
437 var_result.Bind(NullConstant()); | 500 var_result.Bind(NullConstant()); |
438 Goto(&out); | 501 Goto(&out); |
439 } | 502 } |
440 | 503 |
441 BIND(&if_exception); | 504 BIND(&if_exception); |
442 { | 505 { |
443 Node* const pending_exception = | 506 // A stack overflow was detected in RegExp code. |
444 Load(MachineType::AnyTagged(), pending_exception_address); | 507 Node* const pending_exception_address = ExternalConstant( |
445 | 508 ExternalReference(Isolate::kPendingExceptionAddress, isolate())); |
446 // If there is no pending exception, a | 509 CSA_ASSERT(this, IsTheHole(Load(MachineType::AnyTagged(), |
447 // stack overflow (on the backtrack stack) was detected in RegExp code. | 510 pending_exception_address))); |
448 | 511 USE(pending_exception_address); |
449 Label stack_overflow(this), rethrow(this); | |
450 Branch(IsTheHole(pending_exception), &stack_overflow, &rethrow); | |
451 | |
452 BIND(&stack_overflow); | |
453 CallRuntime(Runtime::kThrowStackOverflow, context); | 512 CallRuntime(Runtime::kThrowStackOverflow, context); |
454 Unreachable(); | 513 Unreachable(); |
455 | |
456 BIND(&rethrow); | |
457 CallRuntime(Runtime::kRegExpExecReThrow, context); | |
458 Unreachable(); | |
459 } | 514 } |
460 | 515 |
461 BIND(&runtime); | 516 BIND(&runtime); |
462 { | 517 { |
463 Node* const result = CallRuntime(Runtime::kRegExpExec, context, regexp, | 518 Node* const result = CallRuntime(Runtime::kRegExpExec, context, regexp, |
464 string, last_index, match_info); | 519 string, last_index, match_info); |
465 var_result.Bind(result); | 520 var_result.Bind(result); |
466 Goto(&out); | 521 Goto(&out); |
467 } | 522 } |
468 | 523 |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
564 Node* match_indices; | 619 Node* match_indices; |
565 Label successful_match(this); | 620 Label successful_match(this); |
566 BIND(&run_exec); | 621 BIND(&run_exec); |
567 { | 622 { |
568 // Get last match info from the context. | 623 // Get last match info from the context. |
569 Node* const native_context = LoadNativeContext(context); | 624 Node* const native_context = LoadNativeContext(context); |
570 Node* const last_match_info = LoadContextElement( | 625 Node* const last_match_info = LoadContextElement( |
571 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 626 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
572 | 627 |
573 // Call the exec stub. | 628 // Call the exec stub. |
574 match_indices = IrregexpExec(context, regexp, string, var_lastindex.value(), | 629 match_indices = RegExpExecInternal(context, regexp, string, |
575 last_match_info); | 630 var_lastindex.value(), last_match_info); |
576 var_result.Bind(match_indices); | 631 var_result.Bind(match_indices); |
577 | 632 |
578 // {match_indices} is either null or the RegExpMatchInfo array. | 633 // {match_indices} is either null or the RegExpMatchInfo array. |
579 // Return early if exec failed, possibly updating last index. | 634 // Return early if exec failed, possibly updating last index. |
580 GotoIfNot(WordEqual(match_indices, null), &successful_match); | 635 GotoIfNot(WordEqual(match_indices, null), &successful_match); |
581 | 636 |
582 GotoIfNot(should_update_last_index, if_didnotmatch); | 637 GotoIfNot(should_update_last_index, if_didnotmatch); |
583 | 638 |
584 StoreLastIndex(context, regexp, smi_zero, is_fastpath); | 639 StoreLastIndex(context, regexp, smi_zero, is_fastpath); |
585 Goto(if_didnotmatch); | 640 Goto(if_didnotmatch); |
(...skipping 1511 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2097 // array depending on whether the {regexp} matches. | 2152 // array depending on whether the {regexp} matches. |
2098 { | 2153 { |
2099 Label next(this), if_stringisempty(this, Label::kDeferred); | 2154 Label next(this), if_stringisempty(this, Label::kDeferred); |
2100 Branch(SmiEqual(string_length, smi_zero), &if_stringisempty, &next); | 2155 Branch(SmiEqual(string_length, smi_zero), &if_stringisempty, &next); |
2101 | 2156 |
2102 BIND(&if_stringisempty); | 2157 BIND(&if_stringisempty); |
2103 { | 2158 { |
2104 Node* const last_match_info = LoadContextElement( | 2159 Node* const last_match_info = LoadContextElement( |
2105 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 2160 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
2106 | 2161 |
2107 Node* const match_indices = | 2162 Node* const match_indices = RegExpExecInternal(context, regexp, string, |
2108 IrregexpExec(context, regexp, string, smi_zero, last_match_info); | 2163 smi_zero, last_match_info); |
2109 | 2164 |
2110 Label return_singleton_array(this); | 2165 Label return_singleton_array(this); |
2111 Branch(WordEqual(match_indices, null), &return_singleton_array, | 2166 Branch(WordEqual(match_indices, null), &return_singleton_array, |
2112 &return_empty_array); | 2167 &return_empty_array); |
2113 | 2168 |
2114 BIND(&return_singleton_array); | 2169 BIND(&return_singleton_array); |
2115 { | 2170 { |
2116 Node* const length = SmiConstant(1); | 2171 Node* const length = SmiConstant(1); |
2117 Node* const capacity = IntPtrConstant(1); | 2172 Node* const capacity = IntPtrConstant(1); |
2118 Node* const result = AllocateJSArray(kind, array_map, capacity, length, | 2173 Node* const result = AllocateJSArray(kind, array_map, capacity, length, |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2159 Branch(SmiEqual(next_search_from, string_length), &push_suffix_and_out, | 2214 Branch(SmiEqual(next_search_from, string_length), &push_suffix_and_out, |
2160 &next); | 2215 &next); |
2161 BIND(&next); | 2216 BIND(&next); |
2162 } | 2217 } |
2163 | 2218 |
2164 // Search for the given {regexp}. | 2219 // Search for the given {regexp}. |
2165 | 2220 |
2166 Node* const last_match_info = LoadContextElement( | 2221 Node* const last_match_info = LoadContextElement( |
2167 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 2222 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
2168 | 2223 |
2169 Node* const match_indices = IrregexpExec(context, regexp, string, | 2224 Node* const match_indices = RegExpExecInternal( |
2170 next_search_from, last_match_info); | 2225 context, regexp, string, next_search_from, last_match_info); |
2171 | 2226 |
2172 // We're done if no match was found. | 2227 // We're done if no match was found. |
2173 { | 2228 { |
2174 Label next(this); | 2229 Label next(this); |
2175 Branch(WordEqual(match_indices, null), &push_suffix_and_out, &next); | 2230 Branch(WordEqual(match_indices, null), &push_suffix_and_out, &next); |
2176 BIND(&next); | 2231 BIND(&next); |
2177 } | 2232 } |
2178 | 2233 |
2179 Node* const match_from = LoadFixedArrayElement( | 2234 Node* const match_from = LoadFixedArrayElement( |
2180 match_indices, RegExpMatchInfo::kFirstCaptureIndex); | 2235 match_indices, RegExpMatchInfo::kFirstCaptureIndex); |
(...skipping 660 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2841 Node* const null = NullConstant(); | 2896 Node* const null = NullConstant(); |
2842 Node* const smi_zero = SmiConstant(0); | 2897 Node* const smi_zero = SmiConstant(0); |
2843 | 2898 |
2844 CSA_ASSERT(this, IsJSRegExp(regexp)); | 2899 CSA_ASSERT(this, IsJSRegExp(regexp)); |
2845 CSA_ASSERT(this, IsString(string)); | 2900 CSA_ASSERT(this, IsString(string)); |
2846 | 2901 |
2847 Node* const native_context = LoadNativeContext(context); | 2902 Node* const native_context = LoadNativeContext(context); |
2848 Node* const internal_match_info = LoadContextElement( | 2903 Node* const internal_match_info = LoadContextElement( |
2849 native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX); | 2904 native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX); |
2850 | 2905 |
2851 Node* const match_indices = | 2906 Node* const match_indices = RegExpExecInternal(context, regexp, string, |
2852 IrregexpExec(context, regexp, string, smi_zero, internal_match_info); | 2907 smi_zero, internal_match_info); |
2853 | 2908 |
2854 Label if_matched(this), if_didnotmatch(this); | 2909 Label if_matched(this), if_didnotmatch(this); |
2855 Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); | 2910 Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); |
2856 | 2911 |
2857 BIND(&if_didnotmatch); | 2912 BIND(&if_didnotmatch); |
2858 Return(null); | 2913 Return(null); |
2859 | 2914 |
2860 BIND(&if_matched); | 2915 BIND(&if_matched); |
2861 { | 2916 { |
2862 Node* result = | 2917 Node* result = |
2863 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); | 2918 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); |
2864 Return(result); | 2919 Return(result); |
2865 } | 2920 } |
2866 } | 2921 } |
2867 | 2922 |
2868 } // namespace internal | 2923 } // namespace internal |
2869 } // namespace v8 | 2924 } // namespace v8 |
OLD | NEW |