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

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

Issue 2433923003: [regexp] Add fast-path for global, callable replace (Closed)
Patch Set: Re-add asserts and COW array usage Created 4 years, 2 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 | « no previous file | src/compiler/code-assembler.h » ('j') | src/runtime/runtime-regexp.cc » ('J')
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-utils.h" 5 #include "src/builtins/builtins-utils.h"
6 #include "src/builtins/builtins.h" 6 #include "src/builtins/builtins.h"
7 7
8 #include "src/code-factory.h" 8 #include "src/code-factory.h"
9 #include "src/regexp/jsregexp.h" 9 #include "src/regexp/jsregexp.h"
10 #include "src/regexp/regexp-utils.h" 10 #include "src/regexp/regexp-utils.h"
(...skipping 1428 matching lines...) Expand 10 before | Expand all | Expand 10 after
1439 Handle<String> substr = 1439 Handle<String> substr =
1440 factory->NewSubString(string, prev_string_index, length); 1440 factory->NewSubString(string, prev_string_index, length);
1441 elems = FixedArray::SetAndGrow(elems, num_elems++, substr); 1441 elems = FixedArray::SetAndGrow(elems, num_elems++, substr);
1442 } 1442 }
1443 1443
1444 return *NewJSArrayWithElements(isolate, elems, num_elems); 1444 return *NewJSArrayWithElements(isolate, elems, num_elems);
1445 } 1445 }
1446 1446
1447 namespace { 1447 namespace {
1448 1448
1449 compiler::Node* ReplaceFastPath(CodeStubAssembler* a, compiler::Node* context, 1449 compiler::Node* ReplaceGlobalCallableFastPath(
1450 compiler::Node* regexp, 1450 CodeStubAssembler* a, compiler::Node* context, compiler::Node* regexp,
1451 compiler::Node* subject_string, 1451 compiler::Node* subject_string, compiler::Node* replace_callable) {
1452 compiler::Node* replace_string) { 1452 // The fast path is reached only if {receiver} is a global unmodified
1453 // JSRegExp instance and {replace_callable} is callable.
1454
1455 typedef CodeStubAssembler::Variable Variable;
1456 typedef CodeStubAssembler::Label Label;
1457 typedef compiler::Node Node;
1458
1459 Isolate* const isolate = a->isolate();
1460
1461 Node* const null = a->NullConstant();
1462 Node* const undefined = a->UndefinedConstant();
1463 Node* const int_zero = a->IntPtrConstant(0);
1464 Node* const int_one = a->IntPtrConstant(1);
1465 Node* const smi_zero = a->SmiConstant(Smi::kZero);
1466
1467 Node* const native_context = a->LoadNativeContext(context);
1468
1469 Label out(a);
1470 Variable var_result(a, MachineRepresentation::kTagged);
1471
1472 // Set last index to 0.
1473 FastStoreLastIndex(a, context, regexp, smi_zero);
1474
1475 // Allocate {result_array}.
1476 Node* result_array;
1477 {
1478 ElementsKind kind = FAST_ELEMENTS;
1479 Node* const array_map = a->LoadJSArrayElementsMap(kind, native_context);
1480 Node* const capacity = a->IntPtrConstant(16);
1481 Node* const length = smi_zero;
1482 Node* const allocation_site = nullptr;
1483 CodeStubAssembler::ParameterMode capacity_mode =
1484 CodeStubAssembler::INTPTR_PARAMETERS;
1485
1486 result_array = a->AllocateJSArray(kind, array_map, capacity, length,
1487 allocation_site, capacity_mode);
1488 }
1489
1490 // Call into runtime for RegExpExecMultiple.
1491 Node* last_match_info = a->LoadContextElement(
1492 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
1493 Node* const res =
1494 a->CallRuntime(Runtime::kRegExpExecMultiple, context, regexp,
1495 subject_string, last_match_info, result_array);
1496
1497 // Reset last index to 0.
1498 FastStoreLastIndex(a, context, regexp, smi_zero);
1499
1500 // If no matches, return the subject string.
1501 var_result.Bind(subject_string);
1502 a->GotoIf(a->WordEqual(res, null), &out);
1503
1504 // Reload last match info since it might have changed.
1505 last_match_info = a->LoadContextElement(
1506 native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
1507
1508 Node* const res_length = a->LoadJSArrayLength(res);
1509 Node* const res_elems = a->LoadElements(res);
1510 a->AssertInstanceType(res_elems, FIXED_ARRAY_TYPE);
1511
1512 CodeStubAssembler::ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS;
1513 Node* const num_capture_registers = a->LoadFixedArrayElement(
1514 last_match_info,
1515 a->IntPtrConstant(RegExpMatchInfo::kNumberOfCapturesIndex), 0, mode);
1516
1517 Label if_hasexplicitcaptures(a), if_noexplicitcaptures(a), create_result(a);
1518 a->Branch(a->SmiEqual(num_capture_registers, a->SmiConstant(Smi::FromInt(2))),
1519 &if_noexplicitcaptures, &if_hasexplicitcaptures);
1520
1521 a->Bind(&if_noexplicitcaptures);
1522 {
1523 // If the number of captures is two then there are no explicit captures in
1524 // the regexp, just the implicit capture that captures the whole match. In
1525 // this case we can simplify quite a bit and end up with something faster.
1526 // The builder will consist of some integers that indicate slices of the
1527 // input string and some replacements that were returned from the replace
1528 // function.
1529
1530 Variable var_match_start(a, MachineRepresentation::kTagged);
1531 var_match_start.Bind(smi_zero);
1532
1533 Node* const end = a->SmiUntag(res_length);
1534 Variable var_i(a, MachineType::PointerRepresentation());
1535 var_i.Bind(int_zero);
1536
1537 Variable* vars[] = {&var_i, &var_match_start};
1538 Label loop(a, 2, vars);
1539 a->Goto(&loop);
1540 a->Bind(&loop);
1541 {
1542 Node* const i = var_i.value();
1543 a->GotoUnless(a->IntPtrLessThan(i, end), &create_result);
1544
1545 CodeStubAssembler::ParameterMode mode =
1546 CodeStubAssembler::INTPTR_PARAMETERS;
1547 Node* const elem = a->LoadFixedArrayElement(res_elems, i, 0, mode);
1548
1549 Label if_issmi(a), if_isstring(a), loop_epilogue(a);
1550 a->Branch(a->TaggedIsSmi(elem), &if_issmi, &if_isstring);
1551
1552 a->Bind(&if_issmi);
1553 {
1554 // Integers represent slices of the original string.
1555 Label if_isnegativeorzero(a), if_ispositive(a);
1556 a->BranchIfSmiLessThanOrEqual(elem, smi_zero, &if_isnegativeorzero,
1557 &if_ispositive);
1558
1559 a->Bind(&if_ispositive);
1560 {
1561 Node* const int_elem = a->SmiUntag(elem);
1562 Node* const new_match_start =
1563 a->IntPtrAdd(a->WordShr(int_elem, a->IntPtrConstant(11)),
1564 a->WordAnd(int_elem, a->IntPtrConstant(0x7ff)));
1565 var_match_start.Bind(a->SmiTag(new_match_start));
1566 a->Goto(&loop_epilogue);
1567 }
1568
1569 a->Bind(&if_isnegativeorzero);
1570 {
1571 Node* const next_i = a->IntPtrAdd(i, int_one);
1572 var_i.Bind(next_i);
1573
1574 Node* const next_elem =
1575 a->LoadFixedArrayElement(res_elems, next_i, 0, mode);
1576
1577 Node* const new_match_start = a->SmiSub(next_elem, elem);
1578 var_match_start.Bind(new_match_start);
1579 a->Goto(&loop_epilogue);
1580 }
1581 }
1582
1583 a->Bind(&if_isstring);
1584 {
1585 a->Assert(a->IsStringInstanceType(a->LoadInstanceType(elem)));
1586
1587 Callable call_callable = CodeFactory::Call(isolate);
1588 Node* const replacement_obj =
1589 a->CallJS(call_callable, context, replace_callable, undefined, elem,
1590 var_match_start.value(), subject_string);
1591
1592 Node* const replacement_str = a->ToString(context, replacement_obj);
1593 a->StoreFixedArrayElement(res_elems, i, replacement_str);
1594
1595 Node* const elem_length = a->LoadStringLength(elem);
1596 Node* const new_match_start =
1597 a->SmiAdd(var_match_start.value(), elem_length);
1598 var_match_start.Bind(new_match_start);
1599
1600 a->Goto(&loop_epilogue);
1601 }
1602
1603 a->Bind(&loop_epilogue);
1604 {
1605 var_i.Bind(a->IntPtrAdd(var_i.value(), int_one));
1606 a->Goto(&loop);
1607 }
1608 }
1609 }
1610
1611 a->Bind(&if_hasexplicitcaptures);
1612 {
1613 CodeStubAssembler::ParameterMode mode =
1614 CodeStubAssembler::INTPTR_PARAMETERS;
1615
1616 Node* const from = int_zero;
1617 Node* const to = a->SmiUntag(res_length);
1618 const int increment = 1;
1619
1620 a->BuildFastLoop(
1621 MachineType::PointerRepresentation(), from, to,
1622 [res_elems, isolate, native_context, context, undefined,
1623 replace_callable, mode](CodeStubAssembler* a, Node* index) {
1624 Node* const elem =
1625 a->LoadFixedArrayElement(res_elems, index, 0, mode);
1626
1627 Label do_continue(a);
1628 a->GotoIf(a->TaggedIsSmi(elem), &do_continue);
1629
1630 // elem must be an Array.
1631 // Use the apply argument as backing for global RegExp properties.
1632
1633 a->AssertInstanceType(elem, JS_ARRAY_TYPE);
1634
1635 // TODO(jgruber): Remove indirection through Call->ReflectApply.
1636 Callable call_callable = CodeFactory::Call(isolate);
1637 Node* const reflect_apply = a->LoadContextElement(
1638 native_context, Context::REFLECT_APPLY_INDEX);
1639
1640 Node* const replacement_obj =
1641 a->CallJS(call_callable, context, reflect_apply, undefined,
1642 replace_callable, undefined, elem);
1643
1644 // Overwrite the i'th element in the results with the string we got
1645 // back from the callback function.
1646
1647 Node* const replacement_str = a->ToString(context, replacement_obj);
1648 a->StoreFixedArrayElement(res_elems, index, replacement_str,
1649 UPDATE_WRITE_BARRIER, mode);
1650
1651 a->Goto(&do_continue);
1652 a->Bind(&do_continue);
1653 },
1654 increment, CodeStubAssembler::IndexAdvanceMode::kPost);
1655
1656 a->Goto(&create_result);
1657 }
1658
1659 a->Bind(&create_result);
1660 {
1661 Node* const result = a->CallRuntime(Runtime::kStringBuilderConcat, context,
1662 res, res_length, subject_string);
1663 var_result.Bind(result);
1664 a->Goto(&out);
1665 }
1666
1667 a->Bind(&out);
1668 return var_result.value();
1669 }
1670
1671 compiler::Node* ReplaceSimpleStringFastPath(CodeStubAssembler* a,
1672 compiler::Node* context,
1673 compiler::Node* regexp,
1674 compiler::Node* subject_string,
1675 compiler::Node* replace_string) {
1453 // The fast path is reached only if {receiver} is an unmodified 1676 // The fast path is reached only if {receiver} is an unmodified
1454 // JSRegExp instance, {replace_value} is non-callable, and 1677 // JSRegExp instance, {replace_value} is non-callable, and
1455 // ToString({replace_value}) does not contain '$', i.e. we're doing a simple 1678 // ToString({replace_value}) does not contain '$', i.e. we're doing a simple
1456 // string replacement. 1679 // string replacement.
1457 1680
1458 typedef CodeStubAssembler::Variable Variable; 1681 typedef CodeStubAssembler::Variable Variable;
1459 typedef CodeStubAssembler::Label Label; 1682 typedef CodeStubAssembler::Label Label;
1460 typedef compiler::Node Node; 1683 typedef compiler::Node Node;
1461 1684
1462 Isolate* const isolate = a->isolate(); 1685 Isolate* const isolate = a->isolate();
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
1573 typedef compiler::Node Node; 1796 typedef compiler::Node Node;
1574 1797
1575 Isolate* const isolate = a->isolate(); 1798 Isolate* const isolate = a->isolate();
1576 1799
1577 Node* const maybe_receiver = a->Parameter(0); 1800 Node* const maybe_receiver = a->Parameter(0);
1578 Node* const maybe_string = a->Parameter(1); 1801 Node* const maybe_string = a->Parameter(1);
1579 Node* const replace_value = a->Parameter(2); 1802 Node* const replace_value = a->Parameter(2);
1580 Node* const context = a->Parameter(5); 1803 Node* const context = a->Parameter(5);
1581 1804
1582 Node* const int_zero = a->IntPtrConstant(0); 1805 Node* const int_zero = a->IntPtrConstant(0);
1583 Node* const smi_zero = a->SmiConstant(Smi::kZero);
1584 1806
1585 // Ensure {receiver} is a JSReceiver. 1807 // Ensure {receiver} is a JSReceiver.
1586 Node* const map = 1808 Node* const map =
1587 ThrowIfNotJSReceiver(a, isolate, context, maybe_receiver, 1809 ThrowIfNotJSReceiver(a, isolate, context, maybe_receiver,
1588 MessageTemplate::kIncompatibleMethodReceiver, 1810 MessageTemplate::kIncompatibleMethodReceiver,
1589 "RegExp.prototype.@@replace"); 1811 "RegExp.prototype.@@replace");
1590 Node* const receiver = maybe_receiver; 1812 Node* const receiver = maybe_receiver;
1591 1813
1592 // Convert {maybe_string} to a String. 1814 // Convert {maybe_string} to a String.
1593 Callable tostring_callable = CodeFactory::ToString(isolate); 1815 Callable tostring_callable = CodeFactory::ToString(isolate);
1594 Node* const string = a->CallStub(tostring_callable, context, maybe_string); 1816 Node* const string = a->CallStub(tostring_callable, context, maybe_string);
1595 1817
1596 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance? 1818 // Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
1597 Label checkreplacecallable(a), runtime(a, Label::kDeferred), fastpath(a); 1819 Label checkreplacecallable(a), runtime(a, Label::kDeferred), fastpath(a);
1598 BranchIfFastPath(a, context, map, &checkreplacecallable, &runtime); 1820 BranchIfFastPath(a, context, map, &checkreplacecallable, &runtime);
1599 1821
1600 a->Bind(&checkreplacecallable); 1822 a->Bind(&checkreplacecallable);
1601 Node* const regexp = receiver; 1823 Node* const regexp = receiver;
1602 1824
1603 // 2. Is {replace_value} callable? 1825 // 2. Is {replace_value} callable?
1604 Label checkreplacestring(a), if_iscallable(a, Label::kDeferred); 1826 Label checkreplacestring(a), if_iscallable(a);
1605 a->GotoIf(a->TaggedIsSmi(replace_value), &checkreplacestring); 1827 a->GotoIf(a->TaggedIsSmi(replace_value), &checkreplacestring);
1606 1828
1607 Node* const replace_value_map = a->LoadMap(replace_value); 1829 Node* const replace_value_map = a->LoadMap(replace_value);
1608 a->Branch( 1830 a->Branch(
1609 a->Word32Equal(a->Word32And(a->LoadMapBitField(replace_value_map), 1831 a->Word32Equal(a->Word32And(a->LoadMapBitField(replace_value_map),
1610 a->Int32Constant(1 << Map::kIsCallable)), 1832 a->Int32Constant(1 << Map::kIsCallable)),
1611 a->Int32Constant(0)), 1833 a->Int32Constant(0)),
1612 &checkreplacestring, &if_iscallable); 1834 &checkreplacestring, &if_iscallable);
1613 1835
1614 // 3. Does ToString({replace_value}) contain '$'? 1836 // 3. Does ToString({replace_value}) contain '$'?
1615 a->Bind(&checkreplacestring); 1837 a->Bind(&checkreplacestring);
1616 { 1838 {
1617 Node* const replace_string = 1839 Node* const replace_string =
1618 a->CallStub(tostring_callable, context, replace_value); 1840 a->CallStub(tostring_callable, context, replace_value);
1619 1841
1620 Node* const dollar_char = a->IntPtrConstant('$'); 1842 Node* const dollar_char = a->IntPtrConstant('$');
1621 Node* const smi_minusone = a->SmiConstant(Smi::FromInt(-1)); 1843 Node* const smi_minusone = a->SmiConstant(Smi::FromInt(-1));
1622 a->GotoUnless(a->SmiEqual(a->StringIndexOfChar(context, replace_string, 1844 a->GotoUnless(a->SmiEqual(a->StringIndexOfChar(context, replace_string,
1623 dollar_char, int_zero), 1845 dollar_char, int_zero),
1624 smi_minusone), 1846 smi_minusone),
1625 &runtime); 1847 &runtime);
1626 1848
1627 a->Return(ReplaceFastPath(a, context, regexp, string, replace_string)); 1849 a->Return(ReplaceSimpleStringFastPath(a, context, regexp, string,
1850 replace_string));
1628 } 1851 }
1629 1852
1630 // {regexp} is unmodified and {replace_value} is callable. 1853 // {regexp} is unmodified and {replace_value} is callable.
1631 a->Bind(&if_iscallable); 1854 a->Bind(&if_iscallable);
1632 { 1855 {
1633 Node* const replace_callable = replace_value; 1856 Node* const replace_callable = replace_value;
1634 1857
1635 // Check if the {regexp} is global. 1858 // Check if the {regexp} is global.
1636 Label if_isglobal(a), if_isnotglobal(a); 1859 Label if_isglobal(a), if_isnotglobal(a);
1637 Node* const is_global = FastFlagGetter(a, regexp, JSRegExp::kGlobal); 1860 Node* const is_global = FastFlagGetter(a, regexp, JSRegExp::kGlobal);
1638 a->Branch(is_global, &if_isglobal, &if_isnotglobal); 1861 a->Branch(is_global, &if_isglobal, &if_isnotglobal);
1639 1862
1640 a->Bind(&if_isglobal); 1863 a->Bind(&if_isglobal);
1641 { 1864 {
1642 FastStoreLastIndex(a, context, regexp, smi_zero); 1865 Node* const result = ReplaceGlobalCallableFastPath(
1643 Node* const result = 1866 a, context, regexp, string, replace_callable);
1644 a->CallRuntime(Runtime::kStringReplaceGlobalRegExpWithFunction,
1645 context, string, regexp, replace_callable);
1646 a->Return(result); 1867 a->Return(result);
1647 } 1868 }
1648 1869
1649 a->Bind(&if_isnotglobal); 1870 a->Bind(&if_isnotglobal);
1650 { 1871 {
1651 Node* const result = 1872 Node* const result =
1652 a->CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction, 1873 a->CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction,
1653 context, string, regexp, replace_callable); 1874 context, string, regexp, replace_callable);
1654 a->Return(result); 1875 a->Return(result);
1655 } 1876 }
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
1695 a->Bind(&if_matched); 1916 a->Bind(&if_matched);
1696 { 1917 {
1697 Node* result = ConstructNewResultFromMatchInfo(isolate, a, context, 1918 Node* result = ConstructNewResultFromMatchInfo(isolate, a, context,
1698 match_indices, string); 1919 match_indices, string);
1699 a->Return(result); 1920 a->Return(result);
1700 } 1921 }
1701 } 1922 }
1702 1923
1703 } // namespace internal 1924 } // namespace internal
1704 } // namespace v8 1925 } // namespace v8
OLDNEW
« no previous file with comments | « no previous file | src/compiler/code-assembler.h » ('j') | src/runtime/runtime-regexp.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698