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

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

Issue 2808023002: [regexp] Ensure there are no shape changes on the fast path (Closed)
Patch Set: Rebase and backmerge second part Created 3 years, 8 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 229 matching lines...) Expand 10 before | Expand all | Expand 10 after
240 240
241 Variable var_result(this, MachineRepresentation::kTagged); 241 Variable var_result(this, MachineRepresentation::kTagged);
242 Label out(this); 242 Label out(this);
243 243
244 // Load lastIndex. 244 // Load lastIndex.
245 Variable var_lastindex(this, MachineRepresentation::kTagged); 245 Variable var_lastindex(this, MachineRepresentation::kTagged);
246 { 246 {
247 Node* const regexp_lastindex = LoadLastIndex(context, regexp, is_fastpath); 247 Node* const regexp_lastindex = LoadLastIndex(context, regexp, is_fastpath);
248 var_lastindex.Bind(regexp_lastindex); 248 var_lastindex.Bind(regexp_lastindex);
249 249
250 // Omit ToLength if lastindex is a non-negative smi. 250 if (is_fastpath) {
251 Label call_tolength(this, Label::kDeferred), next(this); 251 // ToLength on a positive smi is a nop and can be skipped.
252 Branch(TaggedIsPositiveSmi(regexp_lastindex), &next, &call_tolength); 252 CSA_ASSERT(this, TaggedIsPositiveSmi(regexp_lastindex));
253 } else {
254 // Omit ToLength if lastindex is a non-negative smi.
255 Label call_tolength(this, Label::kDeferred), next(this);
256 Branch(TaggedIsPositiveSmi(regexp_lastindex), &next, &call_tolength);
253 257
254 Bind(&call_tolength); 258 Bind(&call_tolength);
255 { 259 {
256 Callable tolength_callable = CodeFactory::ToLength(isolate); 260 Callable tolength_callable = CodeFactory::ToLength(isolate);
257 var_lastindex.Bind( 261 var_lastindex.Bind(
258 CallStub(tolength_callable, context, regexp_lastindex)); 262 CallStub(tolength_callable, context, regexp_lastindex));
259 Goto(&next); 263 Goto(&next);
264 }
265
266 Bind(&next);
260 } 267 }
261
262 Bind(&next);
263 } 268 }
264 269
265 // Check whether the regexp is global or sticky, which determines whether we 270 // Check whether the regexp is global or sticky, which determines whether we
266 // update last index later on. 271 // update last index later on.
267 Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset); 272 Node* const flags = LoadObjectField(regexp, JSRegExp::kFlagsOffset);
268 Node* const is_global_or_sticky = WordAnd( 273 Node* const is_global_or_sticky = WordAnd(
269 SmiUntag(flags), IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky)); 274 SmiUntag(flags), IntPtrConstant(JSRegExp::kGlobal | JSRegExp::kSticky));
270 Node* const should_update_last_index = 275 Node* const should_update_last_index =
271 WordNotEqual(is_global_or_sticky, int_zero); 276 WordNotEqual(is_global_or_sticky, int_zero);
272 277
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after
401 406
402 CallRuntime(Runtime::kThrowTypeError, context, message_id, method_name_str, 407 CallRuntime(Runtime::kThrowTypeError, context, message_id, method_name_str,
403 value_str); 408 value_str);
404 Unreachable(); 409 Unreachable();
405 } 410 }
406 411
407 Bind(&out); 412 Bind(&out);
408 return var_value_map.value(); 413 return var_value_map.value();
409 } 414 }
410 415
411 Node* RegExpBuiltinsAssembler::IsInitialRegExpMap(Node* context, Node* map) { 416 Node* RegExpBuiltinsAssembler::IsInitialRegExpMap(Node* context, Node* object,
417 Node* map) {
418 Label out(this);
419 Variable var_result(this, MachineRepresentation::kWord32);
420
412 Node* const native_context = LoadNativeContext(context); 421 Node* const native_context = LoadNativeContext(context);
413 Node* const regexp_fun = 422 Node* const regexp_fun =
414 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); 423 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
415 Node* const initial_map = 424 Node* const initial_map =
416 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); 425 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
417 Node* const has_initialmap = WordEqual(map, initial_map); 426 Node* const has_initialmap = WordEqual(map, initial_map);
418 427
419 return has_initialmap; 428 var_result.Bind(has_initialmap);
429 GotoIfNot(has_initialmap, &out);
430
431 // The smi check is required to omit ToLength(lastIndex) calls with possible
432 // user-code execution on the fast path.
433 Node* const last_index = FastLoadLastIndex(object);
434 var_result.Bind(TaggedIsPositiveSmi(last_index));
435 Goto(&out);
436
437 Bind(&out);
438 return var_result.value();
420 } 439 }
421 440
422 // RegExp fast path implementations rely on unmodified JSRegExp instances. 441 // RegExp fast path implementations rely on unmodified JSRegExp instances.
423 // We use a fairly coarse granularity for this and simply check whether both 442 // We use a fairly coarse granularity for this and simply check whether both
424 // the regexp itself is unmodified (i.e. its map has not changed) and its 443 // the regexp itself is unmodified (i.e. its map has not changed), its
425 // prototype is unmodified. 444 // prototype is unmodified, and lastIndex is a non-negative smi.
426 void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context, 445 void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context,
446 Node* const object,
427 Node* const map, 447 Node* const map,
428 Label* const if_isunmodified, 448 Label* const if_isunmodified,
429 Label* const if_ismodified) { 449 Label* const if_ismodified) {
450 CSA_ASSERT(this, WordEqual(LoadMap(object), map));
451
452 // TODO(ishell): Update this check once map changes for constant field
453 // tracking are landing.
454
430 Node* const native_context = LoadNativeContext(context); 455 Node* const native_context = LoadNativeContext(context);
431 Node* const regexp_fun = 456 Node* const regexp_fun =
432 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); 457 LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
433 Node* const initial_map = 458 Node* const initial_map =
434 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset); 459 LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
435 Node* const has_initialmap = WordEqual(map, initial_map); 460 Node* const has_initialmap = WordEqual(map, initial_map);
436 461
437 GotoIfNot(has_initialmap, if_ismodified); 462 GotoIfNot(has_initialmap, if_ismodified);
438 463
439 Node* const initial_proto_initial_map = 464 Node* const initial_proto_initial_map =
440 LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX); 465 LoadContextElement(native_context, Context::REGEXP_PROTOTYPE_MAP_INDEX);
441 Node* const proto_map = LoadMap(LoadMapPrototype(map)); 466 Node* const proto_map = LoadMap(LoadMapPrototype(map));
442 Node* const proto_has_initialmap = 467 Node* const proto_has_initialmap =
443 WordEqual(proto_map, initial_proto_initial_map); 468 WordEqual(proto_map, initial_proto_initial_map);
444 469
445 // TODO(ishell): Update this check once map changes for constant field 470 GotoIfNot(proto_has_initialmap, if_ismodified);
446 // tracking are landing.
447 471
448 Branch(proto_has_initialmap, if_isunmodified, if_ismodified); 472 // The smi check is required to omit ToLength(lastIndex) calls with possible
473 // user-code execution on the fast path.
474 Node* const last_index = FastLoadLastIndex(object);
475 Branch(TaggedIsPositiveSmi(last_index), if_isunmodified, if_ismodified);
449 } 476 }
450 477
451 Node* RegExpBuiltinsAssembler::IsFastRegExpMap(Node* const context, 478 Node* RegExpBuiltinsAssembler::IsFastRegExpMap(Node* const context,
479 Node* const object,
452 Node* const map) { 480 Node* const map) {
453 Label yup(this), nope(this), out(this); 481 Label yup(this), nope(this), out(this);
454 Variable var_result(this, MachineRepresentation::kWord32); 482 Variable var_result(this, MachineRepresentation::kWord32);
455 483
456 BranchIfFastRegExp(context, map, &yup, &nope); 484 BranchIfFastRegExp(context, object, map, &yup, &nope);
457 485
458 Bind(&yup); 486 Bind(&yup);
459 var_result.Bind(Int32Constant(1)); 487 var_result.Bind(Int32Constant(1));
460 Goto(&out); 488 Goto(&out);
461 489
462 Bind(&nope); 490 Bind(&nope);
463 var_result.Bind(Int32Constant(0)); 491 var_result.Bind(Int32Constant(0));
464 Goto(&out); 492 Goto(&out);
465 493
466 Bind(&out); 494 Bind(&out);
(...skipping 20 matching lines...) Expand all
487 515
488 // Ensure {maybe_receiver} is a JSRegExp. 516 // Ensure {maybe_receiver} is a JSRegExp.
489 Node* const regexp_map = ThrowIfNotInstanceType( 517 Node* const regexp_map = ThrowIfNotInstanceType(
490 context, maybe_receiver, JS_REGEXP_TYPE, "RegExp.prototype.exec"); 518 context, maybe_receiver, JS_REGEXP_TYPE, "RegExp.prototype.exec");
491 Node* const receiver = maybe_receiver; 519 Node* const receiver = maybe_receiver;
492 520
493 // Convert {maybe_string} to a String. 521 // Convert {maybe_string} to a String.
494 Node* const string = ToString(context, maybe_string); 522 Node* const string = ToString(context, maybe_string);
495 523
496 Label if_isfastpath(this), if_isslowpath(this); 524 Label if_isfastpath(this), if_isslowpath(this);
497 Branch(IsInitialRegExpMap(context, regexp_map), &if_isfastpath, 525 Branch(IsInitialRegExpMap(context, receiver, regexp_map), &if_isfastpath,
498 &if_isslowpath); 526 &if_isslowpath);
499 527
500 Bind(&if_isfastpath); 528 Bind(&if_isfastpath);
501 { 529 {
502 Node* const result = 530 Node* const result =
503 RegExpPrototypeExecBody(context, receiver, string, true); 531 RegExpPrototypeExecBody(context, receiver, string, true);
504 Return(result); 532 Return(result);
505 } 533 }
506 534
507 Bind(&if_isslowpath); 535 Bind(&if_isslowpath);
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after
674 TF_BUILTIN(RegExpPrototypeFlagsGetter, RegExpBuiltinsAssembler) { 702 TF_BUILTIN(RegExpPrototypeFlagsGetter, RegExpBuiltinsAssembler) {
675 Node* const maybe_receiver = Parameter(0); 703 Node* const maybe_receiver = Parameter(0);
676 Node* const context = Parameter(3); 704 Node* const context = Parameter(3);
677 705
678 Node* const map = ThrowIfNotJSReceiver(context, maybe_receiver, 706 Node* const map = ThrowIfNotJSReceiver(context, maybe_receiver,
679 MessageTemplate::kRegExpNonObject, 707 MessageTemplate::kRegExpNonObject,
680 "RegExp.prototype.flags"); 708 "RegExp.prototype.flags");
681 Node* const receiver = maybe_receiver; 709 Node* const receiver = maybe_receiver;
682 710
683 Label if_isfastpath(this), if_isslowpath(this, Label::kDeferred); 711 Label if_isfastpath(this), if_isslowpath(this, Label::kDeferred);
684 Branch(IsInitialRegExpMap(context, map), &if_isfastpath, &if_isslowpath); 712 Branch(IsInitialRegExpMap(context, receiver, map), &if_isfastpath,
713 &if_isslowpath);
685 714
686 Bind(&if_isfastpath); 715 Bind(&if_isfastpath);
687 Return(FlagsGetter(context, receiver, true)); 716 Return(FlagsGetter(context, receiver, true));
688 717
689 Bind(&if_isslowpath); 718 Bind(&if_isslowpath);
690 Return(FlagsGetter(context, receiver, false)); 719 Return(FlagsGetter(context, receiver, false));
691 } 720 }
692 721
693 // ES#sec-regexp-pattern-flags 722 // ES#sec-regexp-pattern-flags
694 // RegExp ( pattern, flags ) 723 // RegExp ( pattern, flags )
(...skipping 519 matching lines...) Expand 10 before | Expand all | Expand 10 after
1214 Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp, 1243 Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp,
1215 Node* string) { 1244 Node* string) {
1216 Isolate* isolate = this->isolate(); 1245 Isolate* isolate = this->isolate();
1217 1246
1218 Node* const null = NullConstant(); 1247 Node* const null = NullConstant();
1219 1248
1220 Variable var_result(this, MachineRepresentation::kTagged); 1249 Variable var_result(this, MachineRepresentation::kTagged);
1221 Label out(this), if_isfastpath(this), if_isslowpath(this); 1250 Label out(this), if_isfastpath(this), if_isslowpath(this);
1222 1251
1223 Node* const map = LoadMap(regexp); 1252 Node* const map = LoadMap(regexp);
1224 BranchIfFastRegExp(context, map, &if_isfastpath, &if_isslowpath); 1253 BranchIfFastRegExp(context, regexp, map, &if_isfastpath, &if_isslowpath);
1225 1254
1226 Bind(&if_isfastpath); 1255 Bind(&if_isfastpath);
1227 { 1256 {
1228 Node* const result = RegExpPrototypeExecBody(context, regexp, string, true); 1257 Node* const result = RegExpPrototypeExecBody(context, regexp, string, true);
1229 var_result.Bind(result); 1258 var_result.Bind(result);
1230 Goto(&out); 1259 Goto(&out);
1231 } 1260 }
1232 1261
1233 Bind(&if_isslowpath); 1262 Bind(&if_isslowpath);
1234 { 1263 {
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
1288 // Ensure {maybe_receiver} is a JSReceiver. 1317 // Ensure {maybe_receiver} is a JSReceiver.
1289 Node* const map = ThrowIfNotJSReceiver( 1318 Node* const map = ThrowIfNotJSReceiver(
1290 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, 1319 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver,
1291 "RegExp.prototype.test"); 1320 "RegExp.prototype.test");
1292 Node* const receiver = maybe_receiver; 1321 Node* const receiver = maybe_receiver;
1293 1322
1294 // Convert {maybe_string} to a String. 1323 // Convert {maybe_string} to a String.
1295 Node* const string = ToString(context, maybe_string); 1324 Node* const string = ToString(context, maybe_string);
1296 1325
1297 Label fast_path(this), slow_path(this); 1326 Label fast_path(this), slow_path(this);
1298 BranchIfFastRegExp(context, map, &fast_path, &slow_path); 1327 BranchIfFastRegExp(context, receiver, map, &fast_path, &slow_path);
1299 1328
1300 Bind(&fast_path); 1329 Bind(&fast_path);
1301 { 1330 {
1302 Label if_didnotmatch(this); 1331 Label if_didnotmatch(this);
1303 RegExpPrototypeExecBodyWithoutResult(context, receiver, string, 1332 RegExpPrototypeExecBodyWithoutResult(context, receiver, string,
1304 &if_didnotmatch, true); 1333 &if_didnotmatch, true);
1305 Return(TrueConstant()); 1334 Return(TrueConstant());
1306 1335
1307 Bind(&if_didnotmatch); 1336 Bind(&if_didnotmatch);
1308 Return(FalseConstant()); 1337 Return(FalseConstant());
(...skipping 314 matching lines...) Expand 10 before | Expand all | Expand 10 after
1623 // Store the match, growing the fixed array if needed. 1652 // Store the match, growing the fixed array if needed.
1624 1653
1625 array.Push(match); 1654 array.Push(match);
1626 1655
1627 // Advance last index if the match is the empty string. 1656 // Advance last index if the match is the empty string.
1628 1657
1629 Node* const match_length = LoadStringLength(match); 1658 Node* const match_length = LoadStringLength(match);
1630 GotoIfNot(SmiEqual(match_length, smi_zero), &loop); 1659 GotoIfNot(SmiEqual(match_length, smi_zero), &loop);
1631 1660
1632 Node* last_index = LoadLastIndex(context, regexp, is_fastpath); 1661 Node* last_index = LoadLastIndex(context, regexp, is_fastpath);
1633 1662 if (is_fastpath) {
1634 Callable tolength_callable = CodeFactory::ToLength(isolate); 1663 CSA_ASSERT(this, TaggedIsPositiveSmi(last_index));
1635 last_index = CallStub(tolength_callable, context, last_index); 1664 } else {
1665 Callable tolength_callable = CodeFactory::ToLength(isolate);
1666 last_index = CallStub(tolength_callable, context, last_index);
1667 }
1636 1668
1637 Node* const new_last_index = 1669 Node* const new_last_index =
1638 AdvanceStringIndex(string, last_index, is_unicode); 1670 AdvanceStringIndex(string, last_index, is_unicode);
1639 1671
1672 if (is_fastpath) {
1673 // On the fast path, we can be certain that lastIndex can never be
1674 // incremented to overflow the Smi range since the maximal string
1675 // length is less than the maximal Smi value.
1676 STATIC_ASSERT(String::kMaxLength < Smi::kMaxValue);
1677 CSA_ASSERT(this, TaggedIsPositiveSmi(new_last_index));
1678 }
1679
1640 StoreLastIndex(context, regexp, new_last_index, is_fastpath); 1680 StoreLastIndex(context, regexp, new_last_index, is_fastpath);
1641 1681
1642 Goto(&loop); 1682 Goto(&loop);
1643 } 1683 }
1644 } 1684 }
1645 1685
1646 Bind(&out); 1686 Bind(&out);
1647 { 1687 {
1648 // Wrap the match in a JSArray. 1688 // Wrap the match in a JSArray.
1649 1689
(...skipping 13 matching lines...) Expand all
1663 // Ensure {maybe_receiver} is a JSReceiver. 1703 // Ensure {maybe_receiver} is a JSReceiver.
1664 Node* const map = ThrowIfNotJSReceiver( 1704 Node* const map = ThrowIfNotJSReceiver(
1665 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, 1705 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver,
1666 "RegExp.prototype.@@match"); 1706 "RegExp.prototype.@@match");
1667 Node* const receiver = maybe_receiver; 1707 Node* const receiver = maybe_receiver;
1668 1708
1669 // Convert {maybe_string} to a String. 1709 // Convert {maybe_string} to a String.
1670 Node* const string = ToString(context, maybe_string); 1710 Node* const string = ToString(context, maybe_string);
1671 1711
1672 Label fast_path(this), slow_path(this); 1712 Label fast_path(this), slow_path(this);
1673 BranchIfFastRegExp(context, map, &fast_path, &slow_path); 1713 BranchIfFastRegExp(context, receiver, map, &fast_path, &slow_path);
1674 1714
1675 Bind(&fast_path); 1715 Bind(&fast_path);
1676 RegExpPrototypeMatchBody(context, receiver, string, true); 1716 RegExpPrototypeMatchBody(context, receiver, string, true);
1677 1717
1678 Bind(&slow_path); 1718 Bind(&slow_path);
1679 RegExpPrototypeMatchBody(context, receiver, string, false); 1719 RegExpPrototypeMatchBody(context, receiver, string, false);
1680 } 1720 }
1681 1721
1682 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast( 1722 void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast(
1683 Node* const context, Node* const regexp, Node* const string) { 1723 Node* const context, Node* const regexp, Node* const string) {
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
1788 // Ensure {maybe_receiver} is a JSReceiver. 1828 // Ensure {maybe_receiver} is a JSReceiver.
1789 Node* const map = ThrowIfNotJSReceiver( 1829 Node* const map = ThrowIfNotJSReceiver(
1790 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, 1830 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver,
1791 "RegExp.prototype.@@search"); 1831 "RegExp.prototype.@@search");
1792 Node* const receiver = maybe_receiver; 1832 Node* const receiver = maybe_receiver;
1793 1833
1794 // Convert {maybe_string} to a String. 1834 // Convert {maybe_string} to a String.
1795 Node* const string = ToString(context, maybe_string); 1835 Node* const string = ToString(context, maybe_string);
1796 1836
1797 Label fast_path(this), slow_path(this); 1837 Label fast_path(this), slow_path(this);
1798 BranchIfFastRegExp(context, map, &fast_path, &slow_path); 1838 BranchIfFastRegExp(context, receiver, map, &fast_path, &slow_path);
1799 1839
1800 Bind(&fast_path); 1840 Bind(&fast_path);
1801 RegExpPrototypeSearchBodyFast(context, receiver, string); 1841 RegExpPrototypeSearchBodyFast(context, receiver, string);
1802 1842
1803 Bind(&slow_path); 1843 Bind(&slow_path);
1804 RegExpPrototypeSearchBodySlow(context, receiver, string); 1844 RegExpPrototypeSearchBodySlow(context, receiver, string);
1805 } 1845 }
1806 1846
1807 // Generates the fast path for @@split. {regexp} is an unmodified JSRegExp, 1847 // Generates the fast path for @@split. {regexp} is an unmodified JSRegExp,
1808 // {string} is a String, and {limit} is a Smi. 1848 // {string} is a String, and {limit} is a Smi.
(...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after
2052 2092
2053 // Helper that skips a few initial checks. 2093 // Helper that skips a few initial checks.
2054 TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) { 2094 TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) {
2055 typedef RegExpSplitDescriptor Descriptor; 2095 typedef RegExpSplitDescriptor Descriptor;
2056 2096
2057 Node* const regexp = Parameter(Descriptor::kReceiver); 2097 Node* const regexp = Parameter(Descriptor::kReceiver);
2058 Node* const string = Parameter(Descriptor::kString); 2098 Node* const string = Parameter(Descriptor::kString);
2059 Node* const maybe_limit = Parameter(Descriptor::kLimit); 2099 Node* const maybe_limit = Parameter(Descriptor::kLimit);
2060 Node* const context = Parameter(Descriptor::kContext); 2100 Node* const context = Parameter(Descriptor::kContext);
2061 2101
2062 CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp))); 2102 CSA_ASSERT(this, IsFastRegExpMap(context, regexp, LoadMap(regexp)));
2063 CSA_ASSERT(this, IsString(string)); 2103 CSA_ASSERT(this, IsString(string));
2064 2104
2065 // TODO(jgruber): Even if map checks send us to the fast path, we still need 2105 // TODO(jgruber): Even if map checks send us to the fast path, we still need
2066 // to verify the constructor property and jump to the slow path if it has 2106 // to verify the constructor property and jump to the slow path if it has
2067 // been changed. 2107 // been changed.
2068 2108
2069 // Convert {maybe_limit} to a uint32, capping at the maximal smi value. 2109 // Convert {maybe_limit} to a uint32, capping at the maximal smi value.
2070 Variable var_limit(this, MachineRepresentation::kTagged); 2110 Variable var_limit(this, MachineRepresentation::kTagged, maybe_limit);
2071 Label if_limitissmimax(this), limit_done(this); 2111 Label if_limitissmimax(this), limit_done(this), runtime(this);
2072 2112
2073 GotoIf(IsUndefined(maybe_limit), &if_limitissmimax); 2113 GotoIf(IsUndefined(maybe_limit), &if_limitissmimax);
2114 GotoIf(TaggedIsPositiveSmi(maybe_limit), &limit_done);
2074 2115
2116 Node* const limit = ToUint32(context, maybe_limit);
2075 { 2117 {
2076 Node* const limit = ToUint32(context, maybe_limit); 2118 // ToUint32(limit) could potentially change the shape of the RegExp
2119 // object. Recheck that we are still on the fast path and bail to runtime
2120 // otherwise.
2121 {
2122 Label next(this);
2123 BranchIfFastRegExp(context, regexp, LoadMap(regexp), &next, &runtime);
2124 Bind(&next);
2125 }
2126
2077 GotoIfNot(TaggedIsSmi(limit), &if_limitissmimax); 2127 GotoIfNot(TaggedIsSmi(limit), &if_limitissmimax);
2078 2128
2079 var_limit.Bind(limit); 2129 var_limit.Bind(limit);
2080 Goto(&limit_done); 2130 Goto(&limit_done);
2081 } 2131 }
2082 2132
2083 Bind(&if_limitissmimax); 2133 Bind(&if_limitissmimax);
2084 { 2134 {
2085 // TODO(jgruber): In this case, we can probably avoid generation of limit 2135 // TODO(jgruber): In this case, we can probably avoid generation of limit
2086 // checks in Generate_RegExpPrototypeSplitBody. 2136 // checks in Generate_RegExpPrototypeSplitBody.
2087 var_limit.Bind(SmiConstant(Smi::kMaxValue)); 2137 var_limit.Bind(SmiConstant(Smi::kMaxValue));
2088 Goto(&limit_done); 2138 Goto(&limit_done);
2089 } 2139 }
2090 2140
2091 Bind(&limit_done); 2141 Bind(&limit_done);
2092 { 2142 {
2093 Node* const limit = var_limit.value(); 2143 Node* const limit = var_limit.value();
2094 RegExpPrototypeSplitBody(context, regexp, string, limit); 2144 RegExpPrototypeSplitBody(context, regexp, string, limit);
2095 } 2145 }
2146
2147 Bind(&runtime);
2148 {
2149 // The runtime call passes in limit to ensure the second ToUint32(limit)
2150 // call is not observable.
2151 CSA_ASSERT(this, IsHeapNumberMap(LoadReceiverMap(limit)));
2152 Return(CallRuntime(Runtime::kRegExpSplit, context, regexp, string, limit));
2153 }
2096 } 2154 }
2097 2155
2098 // ES#sec-regexp.prototype-@@split 2156 // ES#sec-regexp.prototype-@@split
2099 // RegExp.prototype [ @@split ] ( string, limit ) 2157 // RegExp.prototype [ @@split ] ( string, limit )
2100 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) { 2158 TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) {
2101 Node* const maybe_receiver = Parameter(0); 2159 Node* const maybe_receiver = Parameter(0);
2102 Node* const maybe_string = Parameter(1); 2160 Node* const maybe_string = Parameter(1);
2103 Node* const maybe_limit = Parameter(2); 2161 Node* const maybe_limit = Parameter(2);
2104 Node* const context = Parameter(5); 2162 Node* const context = Parameter(5);
2105 2163
2106 // Ensure {maybe_receiver} is a JSReceiver. 2164 // Ensure {maybe_receiver} is a JSReceiver.
2107 Node* const map = ThrowIfNotJSReceiver( 2165 Node* const map = ThrowIfNotJSReceiver(
2108 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, 2166 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver,
2109 "RegExp.prototype.@@split"); 2167 "RegExp.prototype.@@split");
2110 Node* const receiver = maybe_receiver; 2168 Node* const receiver = maybe_receiver;
2111 2169
2112 // Convert {maybe_string} to a String. 2170 // Convert {maybe_string} to a String.
2113 Node* const string = ToString(context, maybe_string); 2171 Node* const string = ToString(context, maybe_string);
2114 2172
2115 Label stub(this), runtime(this, Label::kDeferred); 2173 Label stub(this), runtime(this, Label::kDeferred);
2116 BranchIfFastRegExp(context, map, &stub, &runtime); 2174 BranchIfFastRegExp(context, receiver, map, &stub, &runtime);
2117 2175
2118 Bind(&stub); 2176 Bind(&stub);
2119 Callable split_callable = CodeFactory::RegExpSplit(isolate()); 2177 Callable split_callable = CodeFactory::RegExpSplit(isolate());
2120 Return(CallStub(split_callable, context, receiver, string, maybe_limit)); 2178 Return(CallStub(split_callable, context, receiver, string, maybe_limit));
2121 2179
2122 Bind(&runtime); 2180 Bind(&runtime);
2123 Return(CallRuntime(Runtime::kRegExpSplit, context, receiver, string, 2181 Return(CallRuntime(Runtime::kRegExpSplit, context, receiver, string,
2124 maybe_limit)); 2182 maybe_limit));
2125 } 2183 }
2126 2184
(...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after
2432 2490
2433 // Helper that skips a few initial checks. 2491 // Helper that skips a few initial checks.
2434 TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) { 2492 TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) {
2435 typedef RegExpReplaceDescriptor Descriptor; 2493 typedef RegExpReplaceDescriptor Descriptor;
2436 2494
2437 Node* const regexp = Parameter(Descriptor::kReceiver); 2495 Node* const regexp = Parameter(Descriptor::kReceiver);
2438 Node* const string = Parameter(Descriptor::kString); 2496 Node* const string = Parameter(Descriptor::kString);
2439 Node* const replace_value = Parameter(Descriptor::kReplaceValue); 2497 Node* const replace_value = Parameter(Descriptor::kReplaceValue);
2440 Node* const context = Parameter(Descriptor::kContext); 2498 Node* const context = Parameter(Descriptor::kContext);
2441 2499
2442 CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp))); 2500 CSA_ASSERT(this, IsFastRegExpMap(context, regexp, LoadMap(regexp)));
2443 CSA_ASSERT(this, IsString(string)); 2501 CSA_ASSERT(this, IsString(string));
2444 2502
2445 Label checkreplacestring(this), if_iscallable(this), 2503 Label checkreplacestring(this), if_iscallable(this),
2446 runtime(this, Label::kDeferred); 2504 runtime(this, Label::kDeferred);
2447 2505
2448 // 2. Is {replace_value} callable? 2506 // 2. Is {replace_value} callable?
2449 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring); 2507 GotoIf(TaggedIsSmi(replace_value), &checkreplacestring);
2450 Branch(IsCallableMap(LoadMap(replace_value)), &if_iscallable, 2508 Branch(IsCallableMap(LoadMap(replace_value)), &if_iscallable,
2451 &checkreplacestring); 2509 &checkreplacestring);
2452 2510
2453 // 3. Does ToString({replace_value}) contain '$'? 2511 // 3. Does ToString({replace_value}) contain '$'?
2454 Bind(&checkreplacestring); 2512 Bind(&checkreplacestring);
2455 { 2513 {
2456 Callable tostring_callable = CodeFactory::ToString(isolate()); 2514 Callable tostring_callable = CodeFactory::ToString(isolate());
2457 Node* const replace_string = 2515 Node* const replace_string =
2458 CallStub(tostring_callable, context, replace_value); 2516 CallStub(tostring_callable, context, replace_value);
2459 2517
2518 // ToString(replaceValue) could potentially change the shape of the RegExp
2519 // object. Recheck that we are still on the fast path and bail to runtime
2520 // otherwise.
2521 {
2522 Label next(this);
2523 BranchIfFastRegExp(context, regexp, LoadMap(regexp), &next, &runtime);
2524 Bind(&next);
2525 }
2526
2460 Callable indexof_callable = CodeFactory::StringIndexOf(isolate()); 2527 Callable indexof_callable = CodeFactory::StringIndexOf(isolate());
2461 Node* const dollar_string = HeapConstant( 2528 Node* const dollar_string = HeapConstant(
2462 isolate()->factory()->LookupSingleCharacterStringFromCode('$')); 2529 isolate()->factory()->LookupSingleCharacterStringFromCode('$'));
2463 Node* const dollar_ix = CallStub(indexof_callable, context, replace_string, 2530 Node* const dollar_ix = CallStub(indexof_callable, context, replace_string,
2464 dollar_string, SmiConstant(0)); 2531 dollar_string, SmiConstant(0));
2465 GotoIfNot(SmiEqual(dollar_ix, SmiConstant(-1)), &runtime); 2532 GotoIfNot(SmiEqual(dollar_ix, SmiConstant(-1)), &runtime);
2466 2533
2467 Return( 2534 Return(
2468 ReplaceSimpleStringFastPath(context, regexp, string, replace_string)); 2535 ReplaceSimpleStringFastPath(context, regexp, string, replace_string));
2469 } 2536 }
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
2523 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver, 2590 context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver,
2524 "RegExp.prototype.@@replace"); 2591 "RegExp.prototype.@@replace");
2525 Node* const receiver = maybe_receiver; 2592 Node* const receiver = maybe_receiver;
2526 2593
2527 // Convert {maybe_string} to a String. 2594 // Convert {maybe_string} to a String.
2528 Callable tostring_callable = CodeFactory::ToString(isolate()); 2595 Callable tostring_callable = CodeFactory::ToString(isolate());
2529 Node* const string = CallStub(tostring_callable, context, maybe_string); 2596 Node* const string = CallStub(tostring_callable, context, maybe_string);
2530 2597
2531 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? 2598 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
2532 Label stub(this), runtime(this, Label::kDeferred); 2599 Label stub(this), runtime(this, Label::kDeferred);
2533 BranchIfFastRegExp(context, map, &stub, &runtime); 2600 BranchIfFastRegExp(context, receiver, map, &stub, &runtime);
2534 2601
2535 Bind(&stub); 2602 Bind(&stub);
2536 Callable replace_callable = CodeFactory::RegExpReplace(isolate()); 2603 Callable replace_callable = CodeFactory::RegExpReplace(isolate());
2537 Return(CallStub(replace_callable, context, receiver, string, replace_value)); 2604 Return(CallStub(replace_callable, context, receiver, string, replace_value));
2538 2605
2539 Bind(&runtime); 2606 Bind(&runtime);
2540 Return(CallRuntime(Runtime::kRegExpReplace, context, receiver, string, 2607 Return(CallRuntime(Runtime::kRegExpReplace, context, receiver, string,
2541 replace_value)); 2608 replace_value));
2542 } 2609 }
2543 2610
(...skipping 24 matching lines...) Expand all
2568 Bind(&if_matched); 2635 Bind(&if_matched);
2569 { 2636 {
2570 Node* result = 2637 Node* result =
2571 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string); 2638 ConstructNewResultFromMatchInfo(context, regexp, match_indices, string);
2572 Return(result); 2639 Return(result);
2573 } 2640 }
2574 } 2641 }
2575 2642
2576 } // namespace internal 2643 } // namespace internal
2577 } // namespace v8 2644 } // 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