Commit e683f33d authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

[turbofan] Move String.prototype.{charAt,CharCodeAt} to call reducer

This should improve performance in cases where receiver or argument types
are unknown.

Bug: v8:7127, v8:7092
Change-Id: I72f1fcdc088bc817c1cc42bf27ecee91965b7680
Reviewed-on: https://chromium-review.googlesource.com/846761Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50342}
parent 5b5dcf08
......@@ -1958,120 +1958,6 @@ Node* GetStringWitness(Node* node) {
} // namespace
// ES6 section 21.1.3.1 String.prototype.charAt ( pos )
Reduction JSBuiltinReducer::ReduceStringCharAt(Node* node) {
// We need at least target, receiver and index parameters.
if (node->op()->ValueInputCount() >= 3) {
Node* index = NodeProperties::GetValueInput(node, 2);
Type* index_type = NodeProperties::GetType(index);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
if (index_type->Is(Type::Integral32OrMinusZeroOrNaN())) {
if (Node* receiver = GetStringWitness(node)) {
if (!index_type->Is(Type::Unsigned32())) {
// 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
// be considered out-of-bounds as well, because of the maximal
// String length limit in V8.
STATIC_ASSERT(String::kMaxLength <= kMaxInt);
index = graph()->NewNode(simplified()->NumberToUint32(), index);
}
// Determine the {receiver} length.
Node* receiver_length =
graph()->NewNode(simplified()->StringLength(), receiver);
// Check if {index} is less than {receiver} length.
Node* check = graph()->NewNode(simplified()->NumberLessThan(), index,
receiver_length);
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
check, control);
// Return the character from the {receiver} as single character string.
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* masked_index = graph()->NewNode(
simplified()->MaskIndexWithBound(), index, receiver_length);
Node* vtrue = graph()->NewNode(simplified()->StringCharAt(), receiver,
masked_index, if_true);
// Return the empty string otherwise.
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* vfalse = jsgraph()->EmptyStringConstant();
control = graph()->NewNode(common()->Merge(2), if_true, if_false);
Node* value =
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
vtrue, vfalse, control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
}
}
return NoChange();
}
// ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos )
Reduction JSBuiltinReducer::ReduceStringCharCodeAt(Node* node) {
// We need at least target, receiver and index parameters.
if (node->op()->ValueInputCount() >= 3) {
Node* index = NodeProperties::GetValueInput(node, 2);
Type* index_type = NodeProperties::GetType(index);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
if (index_type->Is(Type::Integral32OrMinusZeroOrNaN())) {
if (Node* receiver = GetStringWitness(node)) {
if (!index_type->Is(Type::Unsigned32())) {
// 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
// be considered out-of-bounds as well, because of the maximal
// String length limit in V8.
STATIC_ASSERT(String::kMaxLength <= kMaxInt);
index = graph()->NewNode(simplified()->NumberToUint32(), index);
}
// Determine the {receiver} length.
Node* receiver_length =
graph()->NewNode(simplified()->StringLength(), receiver);
// Check if {index} is less than {receiver} length.
Node* check = graph()->NewNode(simplified()->NumberLessThan(), index,
receiver_length);
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
check, control);
// Load the character from the {receiver}.
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* masked_index = graph()->NewNode(
simplified()->MaskIndexWithBound(), index, receiver_length);
Node* vtrue = graph()->NewNode(simplified()->StringCharCodeAt(),
receiver, masked_index, if_true);
// Return NaN otherwise.
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* vfalse = jsgraph()->NaNConstant();
control = graph()->NewNode(common()->Merge(2), if_true, if_false);
Node* value =
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
vtrue, vfalse, control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
}
}
return NoChange();
}
// ES6 String.prototype.concat(...args)
// #sec-string.prototype.concat
Reduction JSBuiltinReducer::ReduceStringConcat(Node* node) {
......@@ -2573,10 +2459,6 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
case kStringFromCharCode:
reduction = ReduceStringFromCharCode(node);
break;
case kStringCharAt:
return ReduceStringCharAt(node);
case kStringCharCodeAt:
return ReduceStringCharCodeAt(node);
case kStringConcat:
return ReduceStringConcat(node);
case kStringIterator:
......
......@@ -108,8 +108,6 @@ class V8_EXPORT_PRIVATE JSBuiltinReducer final
Reduction ReduceNumberIsSafeInteger(Node* node);
Reduction ReduceNumberParseInt(Node* node);
Reduction ReduceObjectCreate(Node* node);
Reduction ReduceStringCharAt(Node* node);
Reduction ReduceStringCharCodeAt(Node* node);
Reduction ReduceStringConcat(Node* node);
Reduction ReduceStringFromCharCode(Node* node);
Reduction ReduceStringIterator(Node* node);
......
......@@ -2890,6 +2890,10 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
return ReduceReturnReceiver(node);
case Builtins::kStringPrototypeIndexOf:
return ReduceStringPrototypeIndexOf(function, node);
case Builtins::kStringPrototypeCharAt:
return ReduceStringPrototypeCharAt(node);
case Builtins::kStringPrototypeCharCodeAt:
return ReduceStringPrototypeCharCodeAt(node);
default:
break;
}
......@@ -3757,6 +3761,123 @@ Reduction JSCallReducer::ReduceArrayPrototypeShift(Node* node) {
return NoChange();
}
// ES6 section 21.1.3.1 String.prototype.charAt ( pos )
Reduction JSCallReducer::ReduceStringPrototypeCharAt(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
CallParameters const& p = CallParametersOf(node->op());
if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
return NoChange();
}
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* index = jsgraph()->ZeroConstant();
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
receiver, effect, control);
if (node->op()->ValueInputCount() >= 3) {
index = effect = graph()->NewNode(simplified()->CheckNumber(p.feedback()),
NodeProperties::GetValueInput(node, 2),
effect, control);
// 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
// be considered out-of-bounds as well, because of the maximal
// String length limit in V8.
STATIC_ASSERT(String::kMaxLength <= kMaxInt);
index = graph()->NewNode(simplified()->NumberToUint32(), index);
}
// Determine the {receiver} length.
Node* receiver_length =
graph()->NewNode(simplified()->StringLength(), receiver);
// Check if {index} is less than {receiver} length.
Node* check =
graph()->NewNode(simplified()->NumberLessThan(), index, receiver_length);
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
// Return the character from the {receiver} as single character string.
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* masked_index = graph()->NewNode(simplified()->MaskIndexWithBound(),
index, receiver_length);
Node* vtrue = graph()->NewNode(simplified()->StringCharAt(), receiver,
masked_index, if_true);
// Return the empty string otherwise.
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* vfalse = jsgraph()->EmptyStringConstant();
control = graph()->NewNode(common()->Merge(2), if_true, if_false);
Node* value = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, 2), vtrue, vfalse, control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
// ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos )
Reduction JSCallReducer::ReduceStringPrototypeCharCodeAt(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
CallParameters const& p = CallParametersOf(node->op());
if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
return NoChange();
}
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* index = jsgraph()->ZeroConstant();
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
receiver, effect, control);
if (node->op()->ValueInputCount() >= 3) {
index = effect = graph()->NewNode(simplified()->CheckNumber(p.feedback()),
NodeProperties::GetValueInput(node, 2),
effect, control);
// 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
// be considered out-of-bounds as well, because of the maximal
// String length limit in V8.
STATIC_ASSERT(String::kMaxLength <= kMaxInt);
index = graph()->NewNode(simplified()->NumberToUint32(), index);
}
// Determine the {receiver} length.
Node* receiver_length =
graph()->NewNode(simplified()->StringLength(), receiver);
// Check if {index} is less than {receiver} length.
Node* check =
graph()->NewNode(simplified()->NumberLessThan(), index, receiver_length);
Node* branch =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
// Load the character from the {receiver}.
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* masked_index = graph()->NewNode(simplified()->MaskIndexWithBound(),
index, receiver_length);
Node* vtrue = graph()->NewNode(simplified()->StringCharCodeAt(), receiver,
masked_index, if_true);
// Return NaN otherwise.
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
Node* vfalse = jsgraph()->NaNConstant();
control = graph()->NewNode(common()->Merge(2), if_true, if_false);
Node* value = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, 2), vtrue, vfalse, control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); }
......
......@@ -95,6 +95,8 @@ class JSCallReducer final : public AdvancedReducer {
Reduction ReduceReturnReceiver(Node* node);
Reduction ReduceStringPrototypeIndexOf(Handle<JSFunction> function,
Node* node);
Reduction ReduceStringPrototypeCharAt(Node* node);
Reduction ReduceStringPrototypeCharCodeAt(Node* node);
Reduction ReduceSoftDeoptimize(Node* node, DeoptimizeReason reason);
......
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