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

Side by Side Diff: src/builtins/builtins-string-gen.cc

Issue 2870013004: [builtins] String.prototype.slice as a CSA builtin. (Closed)
Patch Set: REBASE. Created 3 years, 7 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-definitions.h ('k') | src/code-stub-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 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 #include "src/builtins/builtins-utils-gen.h" 6 #include "src/builtins/builtins-utils-gen.h"
7 #include "src/builtins/builtins.h" 7 #include "src/builtins/builtins.h"
8 #include "src/code-factory.h" 8 #include "src/code-factory.h"
9 #include "src/code-stub-assembler.h" 9 #include "src/code-stub-assembler.h"
10 #include "src/objects.h" 10 #include "src/objects.h"
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after
148 Node* IndexOfDollarChar(Node* const context, Node* const string); 148 Node* IndexOfDollarChar(Node* const context, Node* const string);
149 149
150 Node* IsNullOrUndefined(Node* const value); 150 Node* IsNullOrUndefined(Node* const value);
151 void RequireObjectCoercible(Node* const context, Node* const value, 151 void RequireObjectCoercible(Node* const context, Node* const value,
152 const char* method_name); 152 const char* method_name);
153 153
154 Node* SmiIsNegative(Node* const value) { 154 Node* SmiIsNegative(Node* const value) {
155 return SmiLessThan(value, SmiConstant(0)); 155 return SmiLessThan(value, SmiConstant(0));
156 } 156 }
157 157
158 // substr and slice have a common way of handling the {start} argument.
159 void ConvertAndBoundsCheckStartArgument(Node* context, Variable* var_start,
160 Node* start, Node* string_length) {
161 Node* const start_int =
162 ToInteger(context, start, CodeStubAssembler::kTruncateMinusZero);
163 Node* const zero = SmiConstant(Smi::kZero);
164
165 Label done(this);
166 Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
167 Branch(TaggedIsSmi(start_int), &if_issmi, &if_isheapnumber);
168
169 BIND(&if_issmi);
170 {
171 var_start->Bind(
172 Select(SmiLessThan(start_int, zero),
173 [&] { return SmiMax(SmiAdd(string_length, start_int), zero); },
174 [&] { return start_int; }, MachineRepresentation::kTagged));
175 Goto(&done);
176 }
177
178 BIND(&if_isheapnumber);
179 {
180 // If {start} is a heap number, it is definitely out of bounds. If it is
181 // negative, {start} = max({string_length} + {start}),0) = 0'. If it is
182 // positive, set {start} to {string_length} which ultimately results in
183 // returning an empty string.
184 Node* const float_zero = Float64Constant(0.);
185 Node* const start_float = LoadHeapNumberValue(start_int);
186 var_start->Bind(SelectTaggedConstant(
187 Float64LessThan(start_float, float_zero), zero, string_length));
188 Goto(&done);
189 }
190 BIND(&done);
191 }
192
158 // Implements boilerplate logic for {match, split, replace, search} of the 193 // Implements boilerplate logic for {match, split, replace, search} of the
159 // form: 194 // form:
160 // 195 //
161 // if (!IS_NULL_OR_UNDEFINED(object)) { 196 // if (!IS_NULL_OR_UNDEFINED(object)) {
162 // var maybe_function = object[symbol]; 197 // var maybe_function = object[symbol];
163 // if (!IS_UNDEFINED(maybe_function)) { 198 // if (!IS_UNDEFINED(maybe_function)) {
164 // return %_Call(maybe_function, ...); 199 // return %_Call(maybe_function, ...);
165 // } 200 // }
166 // } 201 // }
167 // 202 //
(...skipping 1118 matching lines...) Expand 10 before | Expand all | Expand 10 after
1286 BIND(&out); 1321 BIND(&out);
1287 { 1322 {
1288 Node* const suffix = CallStub(substring_callable, context, subject_string, 1323 Node* const suffix = CallStub(substring_callable, context, subject_string,
1289 match_end_index, subject_length); 1324 match_end_index, subject_length);
1290 Node* const result = 1325 Node* const result =
1291 CallStub(stringadd_callable, context, var_result.value(), suffix); 1326 CallStub(stringadd_callable, context, var_result.value(), suffix);
1292 Return(result); 1327 Return(result);
1293 } 1328 }
1294 } 1329 }
1295 1330
1331 // ES6 section 21.1.3.18 String.prototype.slice ( start, end )
1332 TF_BUILTIN(StringPrototypeSlice, StringBuiltinsAssembler) {
1333 Label out(this);
1334 VARIABLE(var_start, MachineRepresentation::kTagged);
1335 VARIABLE(var_end, MachineRepresentation::kTagged);
1336
1337 const int kStart = 0;
1338 const int kEnd = 1;
1339 Node* argc =
1340 ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount));
1341 CodeStubArguments args(this, argc);
1342 Node* const receiver = args.GetReceiver();
1343 Node* const start =
1344 args.GetOptionalArgumentValue(kStart, UndefinedConstant());
1345 Node* const end = args.GetOptionalArgumentValue(kEnd, UndefinedConstant());
1346 Node* const context = Parameter(BuiltinDescriptor::kContext);
1347
1348 Node* const smi_zero = SmiConstant(0);
1349
1350 // 1. Let O be ? RequireObjectCoercible(this value).
1351 RequireObjectCoercible(context, receiver, "String.prototype.slice");
1352
1353 // 2. Let S be ? ToString(O).
1354 Callable tostring_callable = CodeFactory::ToString(isolate());
1355 Node* const subject_string = CallStub(tostring_callable, context, receiver);
1356
1357 // 3. Let len be the number of elements in S.
1358 Node* const length = LoadStringLength(subject_string);
1359
1360 // Conversions and bounds-checks for {start}.
1361 ConvertAndBoundsCheckStartArgument(context, &var_start, start, length);
1362
1363 // 5. If end is undefined, let intEnd be len;
1364 var_end.Bind(length);
1365 GotoIf(WordEqual(end, UndefinedConstant()), &out);
1366
1367 // else let intEnd be ? ToInteger(end).
1368 Node* const end_int =
1369 ToInteger(context, end, CodeStubAssembler::kTruncateMinusZero);
1370
1371 // 7. If intEnd < 0, let to be max(len + intEnd, 0);
1372 // otherwise let to be min(intEnd, len).
1373 Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
1374 Branch(TaggedIsSmi(end_int), &if_issmi, &if_isheapnumber);
1375
1376 BIND(&if_issmi);
1377 {
1378 Node* const length_plus_end = SmiAdd(length, end_int);
1379 var_end.Bind(Select(SmiLessThan(end_int, smi_zero),
1380 [&] { return SmiMax(length_plus_end, smi_zero); },
1381 [&] { return SmiMin(length, end_int); },
1382 MachineRepresentation::kTagged));
1383 Goto(&out);
1384 }
1385
1386 BIND(&if_isheapnumber);
1387 {
1388 // If {end} is a heap number, it is definitely out of bounds. If it is
1389 // negative, {int_end} = max({length} + {int_end}),0) = 0'. If it is
1390 // positive, set {int_end} to {length} which ultimately results in
1391 // returning an empty string.
1392 Node* const float_zero = Float64Constant(0.);
1393 Node* const end_float = LoadHeapNumberValue(end_int);
1394 var_end.Bind(SelectTaggedConstant(Float64LessThan(end_float, float_zero),
1395 smi_zero, length));
1396 Goto(&out);
1397 }
1398
1399 Label return_emptystring(this);
1400 BIND(&out);
1401 {
1402 GotoIf(SmiLessThanOrEqual(var_end.value(), var_start.value()),
1403 &return_emptystring);
1404 Node* const result =
1405 SubString(context, subject_string, var_start.value(), var_end.value(),
1406 SubStringFlags::FROM_TO_ARE_BOUNDED);
1407 args.PopAndReturn(result);
1408 }
1409
1410 BIND(&return_emptystring);
1411 args.PopAndReturn(EmptyStringConstant());
1412 }
1413
1296 // ES6 section 21.1.3.19 String.prototype.split ( separator, limit ) 1414 // ES6 section 21.1.3.19 String.prototype.split ( separator, limit )
1297 TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) { 1415 TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) {
1298 Label out(this); 1416 Label out(this);
1299 1417
1300 Node* const receiver = Parameter(Descriptor::kReceiver); 1418 Node* const receiver = Parameter(Descriptor::kReceiver);
1301 Node* const separator = Parameter(Descriptor::kSeparator); 1419 Node* const separator = Parameter(Descriptor::kSeparator);
1302 Node* const limit = Parameter(Descriptor::kLimit); 1420 Node* const limit = Parameter(Descriptor::kLimit);
1303 Node* const context = Parameter(Descriptor::kContext); 1421 Node* const context = Parameter(Descriptor::kContext);
1304 1422
1305 Node* const smi_zero = SmiConstant(0); 1423 Node* const smi_zero = SmiConstant(0);
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
1390 BIND(&next); 1508 BIND(&next);
1391 } 1509 }
1392 1510
1393 Node* const result = 1511 Node* const result =
1394 CallRuntime(Runtime::kStringSplit, context, subject_string, 1512 CallRuntime(Runtime::kStringSplit, context, subject_string,
1395 separator_string, limit_number); 1513 separator_string, limit_number);
1396 Return(result); 1514 Return(result);
1397 } 1515 }
1398 1516
1399 // ES6 #sec-string.prototype.substr 1517 // ES6 #sec-string.prototype.substr
1400 TF_BUILTIN(StringPrototypeSubstr, CodeStubAssembler) { 1518 TF_BUILTIN(StringPrototypeSubstr, StringBuiltinsAssembler) {
1401 Label out(this), handle_length(this); 1519 Label out(this);
1402 1520
1403 VARIABLE(var_start, MachineRepresentation::kTagged); 1521 VARIABLE(var_start, MachineRepresentation::kTagged);
1404 VARIABLE(var_length, MachineRepresentation::kTagged); 1522 VARIABLE(var_length, MachineRepresentation::kTagged);
1405 1523
1406 Node* const receiver = Parameter(Descriptor::kReceiver); 1524 Node* const receiver = Parameter(Descriptor::kReceiver);
1407 Node* const start = Parameter(Descriptor::kStart); 1525 Node* const start = Parameter(Descriptor::kStart);
1408 Node* const length = Parameter(Descriptor::kLength); 1526 Node* const length = Parameter(Descriptor::kLength);
1409 Node* const context = Parameter(Descriptor::kContext); 1527 Node* const context = Parameter(Descriptor::kContext);
1410 1528
1411 Node* const zero = SmiConstant(Smi::kZero); 1529 Node* const zero = SmiConstant(Smi::kZero);
1412 1530
1413 // Check that {receiver} is coercible to Object and convert it to a String. 1531 // Check that {receiver} is coercible to Object and convert it to a String.
1414 Node* const string = 1532 Node* const string =
1415 ToThisString(context, receiver, "String.prototype.substr"); 1533 ToThisString(context, receiver, "String.prototype.substr");
1416 1534
1417 Node* const string_length = LoadStringLength(string); 1535 Node* const string_length = LoadStringLength(string);
1418 1536
1419 // Conversions and bounds-checks for {start}. 1537 // Conversions and bounds-checks for {start}.
1538 ConvertAndBoundsCheckStartArgument(context, &var_start, start, string_length);
1539
1540 // Conversions and bounds-checks for {length}.
1541 Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
1542
1543 // Default to {string_length} if {length} is undefined.
1420 { 1544 {
1421 Node* const start_int = 1545 Label if_isundefined(this, Label::kDeferred), if_isnotundefined(this);
1422 ToInteger(context, start, CodeStubAssembler::kTruncateMinusZero); 1546 Branch(WordEqual(length, UndefinedConstant()), &if_isundefined,
1547 &if_isnotundefined);
1423 1548
1424 Label if_issmi(this), if_isheapnumber(this, Label::kDeferred); 1549 BIND(&if_isundefined);
1425 Branch(TaggedIsSmi(start_int), &if_issmi, &if_isheapnumber); 1550 var_length.Bind(string_length);
1551 Goto(&if_issmi);
1426 1552
1427 BIND(&if_issmi); 1553 BIND(&if_isnotundefined);
1554 var_length.Bind(
1555 ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero));
1556 }
1557
1558 Branch(TaggedIsSmi(var_length.value()), &if_issmi, &if_isheapnumber);
1559
1560 // Set {length} to min(max({length}, 0), {string_length} - {start}
1561 BIND(&if_issmi);
1562 {
1563 Node* const positive_length = SmiMax(var_length.value(), zero);
1564
1565 Node* const minimal_length = SmiSub(string_length, var_start.value());
1566 var_length.Bind(SmiMin(positive_length, minimal_length));
1567
1568 GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out);
1569 Return(EmptyStringConstant());
1570 }
1571
1572 BIND(&if_isheapnumber);
1573 {
1574 // If {length} is a heap number, it is definitely out of bounds. There are
1575 // two cases according to the spec: if it is negative, "" is returned; if
1576 // it is positive, then length is set to {string_length} - {start}.
1577
1578 CSA_ASSERT(this, IsHeapNumberMap(LoadMap(var_length.value())));
1579
1580 Label if_isnegative(this), if_ispositive(this);
1581 Node* const float_zero = Float64Constant(0.);
1582 Node* const length_float = LoadHeapNumberValue(var_length.value());
1583 Branch(Float64LessThan(length_float, float_zero), &if_isnegative,
1584 &if_ispositive);
1585
1586 BIND(&if_isnegative);
1587 Return(EmptyStringConstant());
1588
1589 BIND(&if_ispositive);
1428 { 1590 {
1429 Node* const length_plus_start = SmiAdd(string_length, start_int); 1591 var_length.Bind(SmiSub(string_length, var_start.value()));
1430 var_start.Bind(Select(SmiLessThan(start_int, zero), 1592 GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out);
1431 [&] { return SmiMax(length_plus_start, zero); }, 1593 Return(EmptyStringConstant());
1432 [&] { return start_int; },
1433 MachineRepresentation::kTagged));
1434 Goto(&handle_length);
1435 }
1436
1437 BIND(&if_isheapnumber);
1438 {
1439 // If {start} is a heap number, it is definitely out of bounds. If it is
1440 // negative, {start} = max({string_length} + {start}),0) = 0'. If it is
1441 // positive, set {start} to {string_length} which ultimately results in
1442 // returning an empty string.
1443 Node* const float_zero = Float64Constant(0.);
1444 Node* const start_float = LoadHeapNumberValue(start_int);
1445 var_start.Bind(SelectTaggedConstant(
1446 Float64LessThan(start_float, float_zero), zero, string_length));
1447 Goto(&handle_length);
1448 } 1594 }
1449 } 1595 }
1450 1596
1451 // Conversions and bounds-checks for {length}.
1452 BIND(&handle_length);
1453 {
1454 Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
1455
1456 // Default to {string_length} if {length} is undefined.
1457 {
1458 Label if_isundefined(this, Label::kDeferred), if_isnotundefined(this);
1459 Branch(WordEqual(length, UndefinedConstant()), &if_isundefined,
1460 &if_isnotundefined);
1461
1462 BIND(&if_isundefined);
1463 var_length.Bind(string_length);
1464 Goto(&if_issmi);
1465
1466 BIND(&if_isnotundefined);
1467 var_length.Bind(
1468 ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero));
1469 }
1470
1471 Branch(TaggedIsSmi(var_length.value()), &if_issmi, &if_isheapnumber);
1472
1473 // Set {length} to min(max({length}, 0), {string_length} - {start}
1474 BIND(&if_issmi);
1475 {
1476 Node* const positive_length = SmiMax(var_length.value(), zero);
1477
1478 Node* const minimal_length = SmiSub(string_length, var_start.value());
1479 var_length.Bind(SmiMin(positive_length, minimal_length));
1480
1481 GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out);
1482 Return(EmptyStringConstant());
1483 }
1484
1485 BIND(&if_isheapnumber);
1486 {
1487 // If {length} is a heap number, it is definitely out of bounds. There are
1488 // two cases according to the spec: if it is negative, "" is returned; if
1489 // it is positive, then length is set to {string_length} - {start}.
1490
1491 CSA_ASSERT(this, IsHeapNumberMap(LoadMap(var_length.value())));
1492
1493 Label if_isnegative(this), if_ispositive(this);
1494 Node* const float_zero = Float64Constant(0.);
1495 Node* const length_float = LoadHeapNumberValue(var_length.value());
1496 Branch(Float64LessThan(length_float, float_zero), &if_isnegative,
1497 &if_ispositive);
1498
1499 BIND(&if_isnegative);
1500 Return(EmptyStringConstant());
1501
1502 BIND(&if_ispositive);
1503 {
1504 var_length.Bind(SmiSub(string_length, var_start.value()));
1505 GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out);
1506 Return(EmptyStringConstant());
1507 }
1508 }
1509 }
1510
1511 BIND(&out); 1597 BIND(&out);
1512 { 1598 {
1513 Node* const end = SmiAdd(var_start.value(), var_length.value()); 1599 Node* const end = SmiAdd(var_start.value(), var_length.value());
1514 Node* const result = SubString(context, string, var_start.value(), end); 1600 Node* const result = SubString(context, string, var_start.value(), end);
1515 Return(result); 1601 Return(result);
1516 } 1602 }
1517 } 1603 }
1518 1604
1519 compiler::Node* StringBuiltinsAssembler::ToSmiBetweenZeroAnd(Node* context, 1605 compiler::Node* StringBuiltinsAssembler::ToSmiBetweenZeroAnd(Node* context,
1520 Node* value, 1606 Node* value,
(...skipping 264 matching lines...) Expand 10 before | Expand all | Expand 10 after
1785 CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context, 1871 CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context,
1786 HeapConstant(factory()->NewStringFromAsciiChecked( 1872 HeapConstant(factory()->NewStringFromAsciiChecked(
1787 "String Iterator.prototype.next", TENURED)), 1873 "String Iterator.prototype.next", TENURED)),
1788 iterator); 1874 iterator);
1789 Unreachable(); 1875 Unreachable();
1790 } 1876 }
1791 } 1877 }
1792 1878
1793 } // namespace internal 1879 } // namespace internal
1794 } // namespace v8 1880 } // namespace v8
OLDNEW
« no previous file with comments | « src/builtins/builtins-definitions.h ('k') | src/code-stub-assembler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698