| 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 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); |
| 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, |
| 433 IntPtrConstant(NativeRegExpMacroAssembler::SUCCESS)), |
| 434 &if_success); |
| 435 GotoIf(IntPtrEqual(int_result, |
| 436 IntPtrConstant(NativeRegExpMacroAssembler::FAILURE)), |
| 374 &if_failure); | 437 &if_failure); |
| 375 GotoIf(SmiEqual(result, SmiConstant(NativeRegExpMacroAssembler::EXCEPTION)), | 438 GotoIf(IntPtrEqual(int_result, |
| 439 IntPtrConstant(NativeRegExpMacroAssembler::EXCEPTION)), |
| 376 &if_exception); | 440 &if_exception); |
| 377 | 441 |
| 378 CSA_ASSERT( | 442 CSA_ASSERT(this, |
| 379 this, SmiEqual(result, SmiConstant(NativeRegExpMacroAssembler::RETRY))); | 443 IntPtrEqual(int_result, |
| 444 IntPtrConstant(NativeRegExpMacroAssembler::RETRY))); |
| 380 Goto(&runtime); | 445 Goto(&runtime); |
| 381 } | 446 } |
| 382 | 447 |
| 383 BIND(&if_success); | 448 BIND(&if_success); |
| 384 { | 449 { |
| 385 // Check that the last match info has space for the capture registers and | 450 // Check that the last match info has space for the capture registers and |
| 386 // the additional information. Ensure no overflow in add. | 451 // the additional information. Ensure no overflow in add. |
| 387 STATIC_ASSERT(FixedArray::kMaxLength < kMaxInt - FixedArray::kLengthOffset); | 452 STATIC_ASSERT(FixedArray::kMaxLength < kMaxInt - FixedArray::kLengthOffset); |
| 388 Node* const available_slots = | 453 Node* const available_slots = |
| 389 SmiSub(LoadFixedArrayBaseLength(match_info), | 454 SmiSub(LoadFixedArrayBaseLength(match_info), |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 433 } | 498 } |
| 434 | 499 |
| 435 BIND(&if_failure); | 500 BIND(&if_failure); |
| 436 { | 501 { |
| 437 var_result.Bind(NullConstant()); | 502 var_result.Bind(NullConstant()); |
| 438 Goto(&out); | 503 Goto(&out); |
| 439 } | 504 } |
| 440 | 505 |
| 441 BIND(&if_exception); | 506 BIND(&if_exception); |
| 442 { | 507 { |
| 443 Node* const pending_exception = | 508 // A stack overflow was detected in RegExp code. |
| 444 Load(MachineType::AnyTagged(), pending_exception_address); | 509 #ifdef DEBUG |
| 445 | 510 Node* const pending_exception_address = ExternalConstant( |
| 446 // If there is no pending exception, a | 511 ExternalReference(Isolate::kPendingExceptionAddress, isolate())); |
| 447 // stack overflow (on the backtrack stack) was detected in RegExp code. | 512 CSA_ASSERT(this, IsTheHole(Load(MachineType::AnyTagged(), |
| 448 | 513 pending_exception_address))); |
| 449 Label stack_overflow(this), rethrow(this); | 514 #endif // DEBUG |
| 450 Branch(IsTheHole(pending_exception), &stack_overflow, &rethrow); | |
| 451 | |
| 452 BIND(&stack_overflow); | |
| 453 CallRuntime(Runtime::kThrowStackOverflow, context); | 515 CallRuntime(Runtime::kThrowStackOverflow, context); |
| 454 Unreachable(); | 516 Unreachable(); |
| 455 | |
| 456 BIND(&rethrow); | |
| 457 CallRuntime(Runtime::kRegExpExecReThrow, context); | |
| 458 Unreachable(); | |
| 459 } | 517 } |
| 460 | 518 |
| 461 BIND(&runtime); | 519 BIND(&runtime); |
| 462 { | 520 { |
| 463 Node* const result = CallRuntime(Runtime::kRegExpExec, context, regexp, | 521 Node* const result = CallRuntime(Runtime::kRegExpExec, context, regexp, |
| 464 string, last_index, match_info); | 522 string, last_index, match_info); |
| 465 var_result.Bind(result); | 523 var_result.Bind(result); |
| 466 Goto(&out); | 524 Goto(&out); |
| 467 } | 525 } |
| 468 | 526 |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 564 Node* match_indices; | 622 Node* match_indices; |
| 565 Label successful_match(this); | 623 Label successful_match(this); |
| 566 BIND(&run_exec); | 624 BIND(&run_exec); |
| 567 { | 625 { |
| 568 // Get last match info from the context. | 626 // Get last match info from the context. |
| 569 Node* const native_context = LoadNativeContext(context); | 627 Node* const native_context = LoadNativeContext(context); |
| 570 Node* const last_match_info = LoadContextElement( | 628 Node* const last_match_info = LoadContextElement( |
| 571 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 629 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
| 572 | 630 |
| 573 // Call the exec stub. | 631 // Call the exec stub. |
| 574 match_indices = IrregexpExec(context, regexp, string, var_lastindex.value(), | 632 match_indices = RegExpExecInternal(context, regexp, string, |
| 575 last_match_info); | 633 var_lastindex.value(), last_match_info); |
| 576 var_result.Bind(match_indices); | 634 var_result.Bind(match_indices); |
| 577 | 635 |
| 578 // {match_indices} is either null or the RegExpMatchInfo array. | 636 // {match_indices} is either null or the RegExpMatchInfo array. |
| 579 // Return early if exec failed, possibly updating last index. | 637 // Return early if exec failed, possibly updating last index. |
| 580 GotoIfNot(WordEqual(match_indices, null), &successful_match); | 638 GotoIfNot(WordEqual(match_indices, null), &successful_match); |
| 581 | 639 |
| 582 GotoIfNot(should_update_last_index, if_didnotmatch); | 640 GotoIfNot(should_update_last_index, if_didnotmatch); |
| 583 | 641 |
| 584 StoreLastIndex(context, regexp, smi_zero, is_fastpath); | 642 StoreLastIndex(context, regexp, smi_zero, is_fastpath); |
| 585 Goto(if_didnotmatch); | 643 Goto(if_didnotmatch); |
| (...skipping 1511 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2097 // array depending on whether the {regexp} matches. | 2155 // array depending on whether the {regexp} matches. |
| 2098 { | 2156 { |
| 2099 Label next(this), if_stringisempty(this, Label::kDeferred); | 2157 Label next(this), if_stringisempty(this, Label::kDeferred); |
| 2100 Branch(SmiEqual(string_length, smi_zero), &if_stringisempty, &next); | 2158 Branch(SmiEqual(string_length, smi_zero), &if_stringisempty, &next); |
| 2101 | 2159 |
| 2102 BIND(&if_stringisempty); | 2160 BIND(&if_stringisempty); |
| 2103 { | 2161 { |
| 2104 Node* const last_match_info = LoadContextElement( | 2162 Node* const last_match_info = LoadContextElement( |
| 2105 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 2163 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
| 2106 | 2164 |
| 2107 Node* const match_indices = | 2165 Node* const match_indices = RegExpExecInternal(context, regexp, string, |
| 2108 IrregexpExec(context, regexp, string, smi_zero, last_match_info); | 2166 smi_zero, last_match_info); |
| 2109 | 2167 |
| 2110 Label return_singleton_array(this); | 2168 Label return_singleton_array(this); |
| 2111 Branch(WordEqual(match_indices, null), &return_singleton_array, | 2169 Branch(WordEqual(match_indices, null), &return_singleton_array, |
| 2112 &return_empty_array); | 2170 &return_empty_array); |
| 2113 | 2171 |
| 2114 BIND(&return_singleton_array); | 2172 BIND(&return_singleton_array); |
| 2115 { | 2173 { |
| 2116 Node* const length = SmiConstant(1); | 2174 Node* const length = SmiConstant(1); |
| 2117 Node* const capacity = IntPtrConstant(1); | 2175 Node* const capacity = IntPtrConstant(1); |
| 2118 Node* const result = AllocateJSArray(kind, array_map, capacity, length, | 2176 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, | 2217 Branch(SmiEqual(next_search_from, string_length), &push_suffix_and_out, |
| 2160 &next); | 2218 &next); |
| 2161 BIND(&next); | 2219 BIND(&next); |
| 2162 } | 2220 } |
| 2163 | 2221 |
| 2164 // Search for the given {regexp}. | 2222 // Search for the given {regexp}. |
| 2165 | 2223 |
| 2166 Node* const last_match_info = LoadContextElement( | 2224 Node* const last_match_info = LoadContextElement( |
| 2167 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); | 2225 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX); |
| 2168 | 2226 |
| 2169 Node* const match_indices = IrregexpExec(context, regexp, string, | 2227 Node* const match_indices = RegExpExecInternal( |
| 2170 next_search_from, last_match_info); | 2228 context, regexp, string, next_search_from, last_match_info); |
| 2171 | 2229 |
| 2172 // We're done if no match was found. | 2230 // We're done if no match was found. |
| 2173 { | 2231 { |
| 2174 Label next(this); | 2232 Label next(this); |
| 2175 Branch(WordEqual(match_indices, null), &push_suffix_and_out, &next); | 2233 Branch(WordEqual(match_indices, null), &push_suffix_and_out, &next); |
| 2176 BIND(&next); | 2234 BIND(&next); |
| 2177 } | 2235 } |
| 2178 | 2236 |
| 2179 Node* const match_from = LoadFixedArrayElement( | 2237 Node* const match_from = LoadFixedArrayElement( |
| 2180 match_indices, RegExpMatchInfo::kFirstCaptureIndex); | 2238 match_indices, RegExpMatchInfo::kFirstCaptureIndex); |
| (...skipping 660 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2841 Node* const null = NullConstant(); | 2899 Node* const null = NullConstant(); |
| 2842 Node* const smi_zero = SmiConstant(0); | 2900 Node* const smi_zero = SmiConstant(0); |
| 2843 | 2901 |
| 2844 CSA_ASSERT(this, IsJSRegExp(regexp)); | 2902 CSA_ASSERT(this, IsJSRegExp(regexp)); |
| 2845 CSA_ASSERT(this, IsString(string)); | 2903 CSA_ASSERT(this, IsString(string)); |
| 2846 | 2904 |
| 2847 Node* const native_context = LoadNativeContext(context); | 2905 Node* const native_context = LoadNativeContext(context); |
| 2848 Node* const internal_match_info = LoadContextElement( | 2906 Node* const internal_match_info = LoadContextElement( |
| 2849 native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX); | 2907 native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX); |
| 2850 | 2908 |
| 2851 Node* const match_indices = | 2909 Node* const match_indices = RegExpExecInternal(context, regexp, string, |
| 2852 IrregexpExec(context, regexp, string, smi_zero, internal_match_info); | 2910 smi_zero, internal_match_info); |
| 2853 | 2911 |
| 2854 Label if_matched(this), if_didnotmatch(this); | 2912 Label if_matched(this), if_didnotmatch(this); |
| 2855 Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); | 2913 Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched); |
| 2856 | 2914 |
| 2857 BIND(&if_didnotmatch); | 2915 BIND(&if_didnotmatch); |
| 2858 Return(null); | 2916 Return(null); |
| 2859 | 2917 |
| 2860 BIND(&if_matched); | 2918 BIND(&if_matched); |
| 2861 { | 2919 { |
| 2862 Node* result = | 2920 Node* result = |
| 2863 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); | 2921 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); |
| 2864 Return(result); | 2922 Return(result); |
| 2865 } | 2923 } |
| 2866 } | 2924 } |
| 2867 | 2925 |
| 2868 } // namespace internal | 2926 } // namespace internal |
| 2869 } // namespace v8 | 2927 } // namespace v8 |
| OLD | NEW |