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

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

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

Powered by Google App Engine
This is Rietveld 408576698