Commit 9d8bd055 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Speculatively optimize string character access.

Add a protector cell for string bounds checks that is being used to
protect speculative bounds for String.prototype.charCodeAt and
String.prototype.charAt in TurboFan (and Crankshaft). This way we don't
have the diamond in optimized code, which stands in the way of other
optimizations for charCodeAt that are currently being worked on by
petermarshall@.

BUG=v8:6391
TBR=mlippautz@chromium.org
R=petermarshall@chromium.org

Review-Url: https://codereview.chromium.org/2905623003
Cr-Commit-Position: refs/heads/master@{#45514}
parent bce2a50d
...@@ -656,7 +656,13 @@ TF_BUILTIN(StringPrototypeCharAt, CodeStubAssembler) { ...@@ -656,7 +656,13 @@ TF_BUILTIN(StringPrototypeCharAt, CodeStubAssembler) {
&if_positioninbounds); &if_positioninbounds);
BIND(&return_emptystring); BIND(&return_emptystring);
Return(EmptyStringConstant()); {
// Invalidate the "String Bounds Check" protector.
Node* invalid = SmiConstant(Isolate::kProtectorInvalid);
Node* cell = LoadRoot(Heap::kStringBoundsCheckProtectorRootIndex);
StoreObjectFieldNoWriteBarrier(cell, Cell::kValueOffset, invalid);
Return(EmptyStringConstant());
}
BIND(&if_positioninbounds); BIND(&if_positioninbounds);
} }
...@@ -695,7 +701,13 @@ TF_BUILTIN(StringPrototypeCharCodeAt, CodeStubAssembler) { ...@@ -695,7 +701,13 @@ TF_BUILTIN(StringPrototypeCharCodeAt, CodeStubAssembler) {
&if_positioninbounds); &if_positioninbounds);
BIND(&return_nan); BIND(&return_nan);
Return(NaNConstant()); {
// Invalidate the "String Bounds Check" protector.
Node* invalid = SmiConstant(Isolate::kProtectorInvalid);
Node* cell = LoadRoot(Heap::kStringBoundsCheckProtectorRootIndex);
StoreObjectFieldNoWriteBarrier(cell, Cell::kValueOffset, invalid);
Return(NaNConstant());
}
BIND(&if_positioninbounds); BIND(&if_positioninbounds);
} }
......
...@@ -1922,8 +1922,26 @@ Reduction JSBuiltinReducer::ReduceStringCharAt(Node* node) { ...@@ -1922,8 +1922,26 @@ Reduction JSBuiltinReducer::ReduceStringCharAt(Node* node) {
Node* effect = NodeProperties::GetEffectInput(node); Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node); Node* control = NodeProperties::GetControlInput(node);
if (index_type->Is(Type::Integral32OrMinusZeroOrNaN())) { if (Node* receiver = GetStringWitness(node)) {
if (Node* receiver = GetStringWitness(node)) { if (isolate()->IsStringBoundsCheckIntact()) {
// Determine the {receiver} length.
Node* receiver_length = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForStringLength()), receiver,
effect, control);
// Check that the {index} is within the range of {receiver}.
index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
receiver_length, effect, control);
// Return the character from the {receiver} as single character string.
Node* value = graph()->NewNode(simplified()->StringCharAt(), receiver,
index, control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
if (index_type->Is(Type::Integral32OrMinusZeroOrNaN())) {
if (!index_type->Is(Type::Unsigned32())) { if (!index_type->Is(Type::Unsigned32())) {
// Map -0 and NaN to 0 (as per ToInteger), and the values in // Map -0 and NaN to 0 (as per ToInteger), and the values in
// the [-2^31,-1] range to the [2^31,2^32-1] range, which will // the [-2^31,-1] range to the [2^31,2^32-1] range, which will
...@@ -1976,8 +1994,26 @@ Reduction JSBuiltinReducer::ReduceStringCharCodeAt(Node* node) { ...@@ -1976,8 +1994,26 @@ Reduction JSBuiltinReducer::ReduceStringCharCodeAt(Node* node) {
Node* effect = NodeProperties::GetEffectInput(node); Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node); Node* control = NodeProperties::GetControlInput(node);
if (index_type->Is(Type::Integral32OrMinusZeroOrNaN())) { if (Node* receiver = GetStringWitness(node)) {
if (Node* receiver = GetStringWitness(node)) { if (isolate()->IsStringBoundsCheckIntact()) {
// Determine the {receiver} length.
Node* receiver_length = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForStringLength()), receiver,
effect, control);
// Check that the {index} is within the range of {receiver}.
index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
receiver_length, effect, control);
// Return the character from the {receiver} as character code.
Node* value = graph()->NewNode(simplified()->StringCharCodeAt(),
receiver, index, control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
if (index_type->Is(Type::Integral32OrMinusZeroOrNaN())) {
if (!index_type->Is(Type::Unsigned32())) { if (!index_type->Is(Type::Unsigned32())) {
// Map -0 and NaN to 0 (as per ToInteger), and the values in // Map -0 and NaN to 0 (as per ToInteger), and the values in
// the [-2^31,-1] range to the [2^31,2^32-1] range, which will // the [-2^31,-1] range to the [2^31,2^32-1] range, which will
......
...@@ -8351,7 +8351,7 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall( ...@@ -8351,7 +8351,7 @@ bool HOptimizedGraphBuilder::TryInlineBuiltinMethodCall(
} }
case kStringCharCodeAt: case kStringCharCodeAt:
case kStringCharAt: case kStringCharAt:
if (argument_count == 2) { if (argument_count == 2 && isolate()->IsStringBoundsCheckIntact()) {
HValue* index = Pop(); HValue* index = Pop();
HValue* string = Pop(); HValue* string = Pop();
Drop(1); // Function. Drop(1); // Function.
......
...@@ -2855,6 +2855,10 @@ void Heap::CreateInitialObjects() { ...@@ -2855,6 +2855,10 @@ void Heap::CreateInitialObjects() {
handle(Smi::FromInt(Isolate::kProtectorValid), isolate())); handle(Smi::FromInt(Isolate::kProtectorValid), isolate()));
set_species_protector(*species_cell); set_species_protector(*species_cell);
Handle<Cell> string_bounds_check_cell = factory->NewCell(
handle(Smi::FromInt(Isolate::kProtectorValid), isolate()));
set_string_bounds_check_protector(*string_bounds_check_cell);
cell = factory->NewPropertyCell(); cell = factory->NewPropertyCell();
cell->set_value(Smi::FromInt(Isolate::kProtectorValid)); cell->set_value(Smi::FromInt(Isolate::kProtectorValid));
set_string_length_protector(*cell); set_string_length_protector(*cell);
......
...@@ -172,6 +172,7 @@ using v8::MemoryPressureLevel; ...@@ -172,6 +172,7 @@ using v8::MemoryPressureLevel;
V(PropertyCell, array_protector, ArrayProtector) \ V(PropertyCell, array_protector, ArrayProtector) \
V(Cell, is_concat_spreadable_protector, IsConcatSpreadableProtector) \ V(Cell, is_concat_spreadable_protector, IsConcatSpreadableProtector) \
V(Cell, species_protector, SpeciesProtector) \ V(Cell, species_protector, SpeciesProtector) \
V(Cell, string_bounds_check_protector, StringBoundsCheckProtector) \
V(PropertyCell, string_length_protector, StringLengthProtector) \ V(PropertyCell, string_length_protector, StringLengthProtector) \
V(Cell, fast_array_iteration_protector, FastArrayIterationProtector) \ V(Cell, fast_array_iteration_protector, FastArrayIterationProtector) \
V(PropertyCell, array_iterator_protector, ArrayIteratorProtector) \ V(PropertyCell, array_iterator_protector, ArrayIteratorProtector) \
......
...@@ -133,6 +133,11 @@ bool Isolate::IsArraySpeciesLookupChainIntact() { ...@@ -133,6 +133,11 @@ bool Isolate::IsArraySpeciesLookupChainIntact() {
Smi::cast(species_cell->value())->value() == kProtectorValid; Smi::cast(species_cell->value())->value() == kProtectorValid;
} }
bool Isolate::IsStringBoundsCheckIntact() {
Cell* string_bounds_check_cell = heap()->string_bounds_check_protector();
return string_bounds_check_cell->value() == Smi::FromInt(kProtectorValid);
}
bool Isolate::IsStringLengthOverflowIntact() { bool Isolate::IsStringLengthOverflowIntact() {
PropertyCell* string_length_cell = heap()->string_length_protector(); PropertyCell* string_length_cell = heap()->string_length_protector();
return string_length_cell->value() == Smi::FromInt(kProtectorValid); return string_length_cell->value() == Smi::FromInt(kProtectorValid);
......
...@@ -1041,6 +1041,7 @@ class Isolate { ...@@ -1041,6 +1041,7 @@ class Isolate {
inline bool IsArraySpeciesLookupChainIntact(); inline bool IsArraySpeciesLookupChainIntact();
bool IsIsConcatSpreadableLookupChainIntact(); bool IsIsConcatSpreadableLookupChainIntact();
bool IsIsConcatSpreadableLookupChainIntact(JSReceiver* receiver); bool IsIsConcatSpreadableLookupChainIntact(JSReceiver* receiver);
inline bool IsStringBoundsCheckIntact();
inline bool IsStringLengthOverflowIntact(); inline bool IsStringLengthOverflowIntact();
inline bool IsArrayIteratorLookupChainIntact(); inline bool IsArrayIteratorLookupChainIntact();
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --opt --no-always-opt
(function() {
function foo(i) { return "a".charAt(i); }
assertEquals("a", foo(0));
assertEquals("a", foo(0));
assertEquals("", foo(1));
%OptimizeFunctionOnNextCall(foo);
assertEquals("", foo(1));
assertOptimized(foo);
})();
(function() {
function foo(i) { return "a".charCodeAt(i); }
assertEquals(97, foo(0));
assertEquals(97, foo(0));
assertEquals(NaN, foo(1));
%OptimizeFunctionOnNextCall(foo);
assertEquals(NaN, foo(1));
assertOptimized(foo);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment