Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(502)

Side by Side Diff: src/builtins/builtins-regexp.cc

Issue 2668703002: [stubs] Add RegExpReplace and RegExpSplit stubs (Closed)
Patch Set: BranchIfFastRegExp/IsFastRegExpMap Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/builtins/builtins-regexp.h ('k') | src/builtins/builtins-string.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 the V8 project authors. All rights reserved. 1 // Copyright 2016 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.h" 5 #include "src/builtins/builtins-regexp.h"
6 6
7 #include "src/builtins/builtins-constructor.h" 7 #include "src/builtins/builtins-constructor.h"
8 #include "src/builtins/builtins-utils.h" 8 #include "src/builtins/builtins-utils.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 404 matching lines...) Expand 10 before | Expand all | Expand 10 after
415 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); 415 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
416 Node* const has_initialmap = WordEqual(map, initial_map); 416 Node* const has_initialmap = WordEqual(map, initial_map);
417 417
418 return has_initialmap; 418 return has_initialmap;
419 } 419 }
420 420
421 // RegExp fast path implementations rely on unmodified JSRegExp instances. 421 // RegExp fast path implementations rely on unmodified JSRegExp instances.
422 // We use a fairly coarse granularity for this and simply check whether both 422 // We use a fairly coarse granularity for this and simply check whether both
423 // the regexp itself is unmodified (i.e. its map has not changed) and its 423 // the regexp itself is unmodified (i.e. its map has not changed) and its
424 // prototype is unmodified. 424 // prototype is unmodified.
425 void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* context, Node* map, 425 void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context,
426 Label* if_isunmodified, 426 Node* const map,
427 Label* if_ismodified) { 427 Label* const if_isunmodified,
428 Label* const if_ismodified) {
428 Node* const native_context = LoadNativeContext(context); 429 Node* const native_context = LoadNativeContext(context);
429 Node* const regexp_fun = 430 Node* const regexp_fun =
430 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); 431 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
431 Node* const initial_map = 432 Node* const initial_map =
432 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); 433 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
433 Node* const has_initialmap = WordEqual(map, initial_map); 434 Node* const has_initialmap = WordEqual(map, initial_map);
434 435
435 GotoUnless(has_initialmap, if_ismodified); 436 GotoUnless(has_initialmap, if_ismodified);
436 437
437 Node* const initial_proto_initial_map = 438 Node* const initial_proto_initial_map =
438 LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX); 439 LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX);
439 Node* const proto_map = LoadMap(LoadMapPrototype(map)); 440 Node* const proto_map = LoadMap(LoadMapPrototype(map));
440 Node* const proto_has_initialmap = 441 Node* const proto_has_initialmap =
441 WordEqual(proto_map, initial_proto_initial_map); 442 WordEqual(proto_map, initial_proto_initial_map);
442 443
443 // TODO(ishell): Update this check once map changes for constant field 444 // TODO(ishell): Update this check once map changes for constant field
444 // tracking are landing. 445 // tracking are landing.
445 446
446 Branch(proto_has_initialmap, if_isunmodified, if_ismodified); 447 Branch(proto_has_initialmap, if_isunmodified, if_ismodified);
447 } 448 }
448 449
450 Node* RegExpBuiltinsAssembler::IsFastRegExpMap(Node* const context,
451 Node* const map) {
452 Label yup(this), nope(this), out(this);
453 Variable var_result(this, MachineRepresentation::kWord32);
454
455 BranchIfFastRegExp(context, map, &yup, &nope);
456
457 Bind(&yup);
458 var_result.Bind(Int32Constant(1));
459 Goto(&out);
460
461 Bind(&nope);
462 var_result.Bind(Int32Constant(0));
463 Goto(&out);
464
465 Bind(&out);
466 return var_result.value();
467 }
468
449 void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* context, Node* map, 469 void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* context, Node* map,
450 Label* if_isunmodified, 470 Label* if_isunmodified,
451 Label* if_ismodified) { 471 Label* if_ismodified) {
452 Node* const native_context = LoadNativeContext(context); 472 Node* const native_context = LoadNativeContext(context);
453 Node* const initial_regexp_result_map = 473 Node* const initial_regexp_result_map =
454 LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX); 474 LoadContextElement(native_context, Context::REGEXP_RESULT_MAP_INDEX);
455 475
456 Branch(WordEqual(map, initial_regexp_result_map), if_isunmodified, 476 Branch(WordEqual(map, initial_regexp_result_map), if_isunmodified,
457 if_ismodified); 477 if_ismodified);
458 } 478 }
(...skipping 1563 matching lines...) Expand 10 before | Expand all | Expand 10 after
2022 Bind(&return_empty_array); 2042 Bind(&return_empty_array);
2023 { 2043 {
2024 Node* const length = smi_zero; 2044 Node* const length = smi_zero;
2025 Node* const capacity = int_zero; 2045 Node* const capacity = int_zero;
2026 Node* const result = AllocateJSArray(kind, array_map, capacity, length, 2046 Node* const result = AllocateJSArray(kind, array_map, capacity, length,
2027 allocation_site, mode); 2047 allocation_site, mode);
2028 Return(result); 2048 Return(result);
2029 } 2049 }
2030 } 2050 }
2031 2051
2052 // Helper that skips a few initial checks.
2053 TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) {
2054 typedef RegExpSplitDescriptor Descriptor;
2055
2056 Node* const regexp = Parameter(Descriptor::kReceiver);
2057 Node* const string = Parameter(Descriptor::kString);
2058 Node* const maybe_limit = Parameter(Descriptor::kLimit);
2059 Node* const context = Parameter(Descriptor::kContext);
2060
2061 CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp)));
2062 CSA_ASSERT(this, IsString(string));
2063
2064 // TODO(jgruber): Even if map checks send us to the fast path, we still need
2065 // to verify the constructor property and jump to the slow path if it has
2066 // been changed.
2067
2068 // Convert {maybe_limit} to a uint32, capping at the maximal smi value.
2069 Variable var_limit(this, MachineRepresentation::kTagged);
2070 Label if_limitissmimax(this), limit_done(this);
2071
2072 GotoIf(IsUndefined(maybe_limit), &if_limitissmimax);
2073
2074 {
2075 Node* const limit = ToUint32(context, maybe_limit);
2076 GotoUnless(TaggedIsSmi(limit), &if_limitissmimax);
2077
2078 var_limit.Bind(limit);
2079 Goto(&limit_done);
2080 }
2081
2082 Bind(&if_limitissmimax);
2083 {
2084 // TODO(jgruber): In this case, we can probably avoid generation of limit
2085 // checks in Generate_RegExpPrototypeSplitBody.
2086 var_limit.Bind(SmiConstant(Smi::kMaxValue));
2087 Goto(&limit_done);
2088 }
2089
2090 Bind(&limit_done);
2091 {
2092 Node* const limit = var_limit.value();
2093 RegExpPrototypeSplitBody(context, regexp, string, limit);
2094 }
2095 }
2096
2032 // ES#sec-regexp.prototype-@@split 2097 // ES#sec-regexp.prototype-@@split
2033 // RegExp.prototype [ @@split ] ( string, limit ) 2098 // RegExp.prototype [ @@split ] ( string, limit )
2034 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) { 2099 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) {
2035 Node* const maybe_receiver = Parameter(0); 2100 Node* const maybe_receiver = Parameter(0);
2036 Node* const maybe_string = Parameter(1); 2101 Node* const maybe_string = Parameter(1);
2037 Node* const maybe_limit = Parameter(2); 2102 Node* const maybe_limit = Parameter(2);
2038 Node* const context = Parameter(5); 2103 Node* const context = Parameter(5);
2039 2104
2040 // Ensure {maybe_receiver} is a JSReceiver. 2105 // Ensure {maybe_receiver} is a JSReceiver.
2041 Node* const map = ThrowIfNotJSReceiver( 2106 Node* const map = ThrowIfNotJSReceiver(
2042 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, 2107 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver,
2043 "RegExp.prototype.@@split"); 2108 "RegExp.prototype.@@split");
2044 Node* const receiver = maybe_receiver; 2109 Node* const receiver = maybe_receiver;
2045 2110
2046 // Convert {maybe_string} to a String. 2111 // Convert {maybe_string} to a String.
2047 Node* const string = ToString(context, maybe_string); 2112 Node* const string = ToString(context, maybe_string);
2048 2113
2049 Label fast_path(this), slow_path(this); 2114 Label stub(this), runtime(this, Label::kDeferred);
2050 BranchIfFastRegExp(context, map, &fast_path, &slow_path); 2115 BranchIfFastRegExp(context, map, &stub, &runtime);
2051 2116
2052 Bind(&fast_path); 2117 Bind(&stub);
2053 { 2118 Callable split_callable = CodeFactory::RegExpSplit(isolate());
2054 // TODO(jgruber): Even if map checks send us to the fast path, we still need 2119 Return(CallStub(split_callable, context, receiver, string, maybe_limit));
2055 // to verify the constructor property and jump to the slow path if it has
2056 // been changed.
2057 2120
2058 // Convert {maybe_limit} to a uint32, capping at the maximal smi value. 2121 Bind(&runtime);
2059 Variable var_limit(this, MachineRepresentation::kTagged); 2122 Return(CallRuntime(Runtime::kRegExpSplit, context, receiver, string,
2060 Label if_limitissmimax(this), limit_done(this); 2123 maybe_limit));
2061
2062 GotoIf(IsUndefined(maybe_limit), &if_limitissmimax);
2063
2064 {
2065 Node* const limit = ToUint32(context, maybe_limit);
2066 GotoUnless(TaggedIsSmi(limit), &if_limitissmimax);
2067
2068 var_limit.Bind(limit);
2069 Goto(&limit_done);
2070 }
2071
2072 Bind(&if_limitissmimax);
2073 {
2074 // TODO(jgruber): In this case, we can probably generation of limit checks
2075 // in Generate_RegExpPrototypeSplitBody.
2076 Node* const smi_max = SmiConstant(Smi::kMaxValue);
2077 var_limit.Bind(smi_max);
2078 Goto(&limit_done);
2079 }
2080
2081 Bind(&limit_done);
2082 {
2083 Node* const limit = var_limit.value();
2084 RegExpPrototypeSplitBody(context, receiver, string, limit);
2085 }
2086 }
2087
2088 Bind(&slow_path);
2089 {
2090 Node* const result = CallRuntime(Runtime::kRegExpSplit, context, receiver,
2091 string, maybe_limit);
2092 Return(result);
2093 }
2094 } 2124 }
2095 2125
2096 Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath( 2126 Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath(
2097 Node* context, Node* regexp, Node* string, Node* replace_callable) { 2127 Node* context, Node* regexp, Node* string, Node* replace_callable) {
2098 // The fast path is reached only if {receiver} is a global unmodified 2128 // The fast path is reached only if {receiver} is a global unmodified
2099 // JSRegExp instance and {replace_callable} is callable. 2129 // JSRegExp instance and {replace_callable} is callable.
2100 2130
2101 Isolate* const isolate = this->isolate(); 2131 Isolate* const isolate = this->isolate();
2102 2132
2103 Node* const null = NullConstant(); 2133 Node* const null = NullConstant();
(...skipping 295 matching lines...) Expand 10 before | Expand all | Expand 10 after
2399 var_result.Bind(result); 2429 var_result.Bind(result);
2400 Goto(&out); 2430 Goto(&out);
2401 } 2431 }
2402 } 2432 }
2403 } 2433 }
2404 2434
2405 Bind(&out); 2435 Bind(&out);
2406 return var_result.value(); 2436 return var_result.value();
2407 } 2437 }
2408 2438
2409 // ES#sec-regexp.prototype-@@replace 2439 // Helper that skips a few initial checks.
2410 // RegExp.prototype [ @@replace ] ( string, replaceValue ) 2440 TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) {
2411 TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) { 2441 typedef RegExpReplaceDescriptor Descriptor;
2412 Node* const maybe_receiver = Parameter(0);
2413 Node* const maybe_string = Parameter(1);
2414 Node* const replace_value = Parameter(2);
2415 Node* const context = Parameter(5);
2416 2442
2417 // Ensure {maybe_receiver} is a JSReceiver. 2443 Node* const regexp = Parameter(Descriptor::kReceiver);
2418 Node* const map = ThrowIfNotJSReceiver( 2444 Node* const string = Parameter(Descriptor::kString);
2419 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, 2445 Node* const replace_value = Parameter(Descriptor::kReplaceValue);
2420 "RegExp.prototype.@@replace"); 2446 Node* const context = Parameter(Descriptor::kContext);
2421 Node* const receiver = maybe_receiver;
2422 2447
2423 // Convert {maybe_string} to a String. 2448 CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp)));
2424 Callable tostring_callable = CodeFactory::ToString(isolate()); 2449 CSA_ASSERT(this, IsString(string));
2425 Node* const string = CallStub(tostring_callable, context, maybe_string);
2426 2450
2427 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? 2451 Label checkreplacestring(this), if_iscallable(this),
2428 Label checkreplacecallable(this), runtime(this, Label::kDeferred), 2452 runtime(this, Label::kDeferred);
2429 fastpath(this);
2430 BranchIfFastRegExp(context, map, &checkreplacecallable, &runtime);
2431
2432 Bind(&checkreplacecallable);
2433 Node* const regexp = receiver;
2434 2453
2435 // 2. Is {replace_value} callable? 2454 // 2. Is {replace_value} callable?
2436 Label checkreplacestring(this), if_iscallable(this);
2437 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring); 2455 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring);
2438 2456 Branch(IsCallableMap(LoadMap(replace_value)), &if_iscallable,
2439 Node* const replace_value_map = LoadMap(replace_value); 2457 &checkreplacestring);
2440 Branch(IsCallableMap(replace_value_map), &if_iscallable, &checkreplacestring);
2441 2458
2442 // 3. Does ToString({replace_value}) contain '$'? 2459 // 3. Does ToString({replace_value}) contain '$'?
2443 Bind(&checkreplacestring); 2460 Bind(&checkreplacestring);
2444 { 2461 {
2462 Callable tostring_callable = CodeFactory::ToString(isolate());
2445 Node* const replace_string = 2463 Node* const replace_string =
2446 CallStub(tostring_callable, context, replace_value); 2464 CallStub(tostring_callable, context, replace_value);
2447 2465
2448 Node* const dollar_char = Int32Constant('$'); 2466 Node* const dollar_char = Int32Constant('$');
2449 Node* const smi_minusone = SmiConstant(Smi::FromInt(-1));
2450 GotoUnless(SmiEqual(StringIndexOfChar(context, replace_string, dollar_char, 2467 GotoUnless(SmiEqual(StringIndexOfChar(context, replace_string, dollar_char,
2451 SmiConstant(0)), 2468 SmiConstant(0)),
2452 smi_minusone), 2469 SmiConstant(-1)),
2453 &runtime); 2470 &runtime);
2454 2471
2455 Return( 2472 Return(
2456 ReplaceSimpleStringFastPath(context, regexp, string, replace_string)); 2473 ReplaceSimpleStringFastPath(context, regexp, string, replace_string));
2457 } 2474 }
2458 2475
2459 // {regexp} is unmodified and {replace_value} is callable. 2476 // {regexp} is unmodified and {replace_value} is callable.
2460 Bind(&if_iscallable); 2477 Bind(&if_iscallable);
2461 { 2478 {
2462 Node* const replace_callable = replace_value; 2479 Node* const replace_fn = replace_value;
2463 2480
2464 // Check if the {regexp} is global. 2481 // Check if the {regexp} is global.
2465 Label if_isglobal(this), if_isnotglobal(this); 2482 Label if_isglobal(this), if_isnotglobal(this);
2483
2466 Node* const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal); 2484 Node* const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal);
2467 Branch(is_global, &if_isglobal, &if_isnotglobal); 2485 Branch(is_global, &if_isglobal, &if_isnotglobal);
2468 2486
2469 Bind(&if_isglobal); 2487 Bind(&if_isglobal);
2470 { 2488 Return(ReplaceGlobalCallableFastPath(context, regexp, string, replace_fn));
2471 Node* const result = ReplaceGlobalCallableFastPath(
2472 context, regexp, string, replace_callable);
2473 Return(result);
2474 }
2475 2489
2476 Bind(&if_isnotglobal); 2490 Bind(&if_isnotglobal);
2477 { 2491 Return(CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction,
2478 Node* const result = 2492 context, string, regexp, replace_fn));
2479 CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction,
2480 context, string, regexp, replace_callable);
2481 Return(result);
2482 }
2483 } 2493 }
2484 2494
2485 Bind(&runtime); 2495 Bind(&runtime);
2486 { 2496 Return(CallRuntime(Runtime::kRegExpReplace, context, regexp, string,
2487 Node* const result = CallRuntime(Runtime::kRegExpReplace, context, receiver, 2497 replace_value));
2488 string, replace_value); 2498 }
2489 Return(result); 2499
2490 } 2500 // ES#sec-regexp.prototype-@@replace
2501 // RegExp.prototype [ @@replace ] ( string, replaceValue )
2502 TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) {
2503 Node* const maybe_receiver = Parameter(0);
2504 Node* const maybe_string = Parameter(1);
2505 Node* const replace_value = Parameter(2);
2506 Node* const context = Parameter(5);
2507
2508 // Ensure {maybe_receiver} is a JSReceiver.
2509 Node* const map = ThrowIfNotJSReceiver(
2510 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver,
2511 "RegExp.prototype.@@replace");
2512 Node* const receiver = maybe_receiver;
2513
2514 // Convert {maybe_string} to a String.
2515 Callable tostring_callable = CodeFactory::ToString(isolate());
2516 Node* const string = CallStub(tostring_callable, context, maybe_string);
2517
2518 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
2519 Label stub(this), runtime(this, Label::kDeferred);
2520 BranchIfFastRegExp(context, map, &stub, &runtime);
2521
2522 Bind(&stub);
2523 Callable replace_callable = CodeFactory::RegExpReplace(isolate());
2524 Return(CallStub(replace_callable, context, receiver, string, replace_value));
2525
2526 Bind(&runtime);
2527 Return(CallRuntime(Runtime::kRegExpReplace, context, receiver, string,
2528 replace_value));
2491 } 2529 }
2492 2530
2493 // Simple string matching functionality for internal use which does not modify 2531 // Simple string matching functionality for internal use which does not modify
2494 // the last match info. 2532 // the last match info.
2495 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) { 2533 TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) {
2496 Node* const regexp = Parameter(1); 2534 Node* const regexp = Parameter(1);
2497 Node* const string = Parameter(2); 2535 Node* const string = Parameter(2);
2498 Node* const context = Parameter(5); 2536 Node* const context = Parameter(5);
2499 2537
2500 Node* const null = NullConstant(); 2538 Node* const null = NullConstant();
(...skipping 16 matching lines...) Expand all
2517 Bind(&if_matched); 2555 Bind(&if_matched);
2518 { 2556 {
2519 Node* result = 2557 Node* result =
2520 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); 2558 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string);
2521 Return(result); 2559 Return(result);
2522 } 2560 }
2523 } 2561 }
2524 2562
2525 } // namespace internal 2563 } // namespace internal
2526 } // namespace v8 2564 } // namespace v8
OLDNEW
« no previous file with comments | « src/builtins/builtins-regexp.h ('k') | src/builtins/builtins-string.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698