Commit b975441e authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Introduce dedicated StringIndexOf operator.

The StringIndexOf operation is pure on the JS level, but the actual stub
call must be in the effect chain later so that the Scheduler doesn't
place it inside some allocation region (The %StringIndexOf runtime
function may trigger a GC for string flattening).

BUG=chromium:685580
R=jarin@chromium.org

Review-Url: https://codereview.chromium.org/2657243002
Cr-Commit-Position: refs/heads/master@{#42736}
parent c5644146
...@@ -864,8 +864,6 @@ TF_BUILTIN(StringIndexOf, StringBuiltinsAssembler) { ...@@ -864,8 +864,6 @@ TF_BUILTIN(StringIndexOf, StringBuiltinsAssembler) {
Node* search_string = Parameter(1); Node* search_string = Parameter(1);
Node* position = Parameter(2); Node* position = Parameter(2);
Label call_runtime(this);
Node* instance_type = LoadInstanceType(receiver); Node* instance_type = LoadInstanceType(receiver);
Node* search_string_instance_type = LoadInstanceType(search_string); Node* search_string_instance_type = LoadInstanceType(search_string);
......
...@@ -750,6 +750,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, ...@@ -750,6 +750,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kStringFromCodePoint: case IrOpcode::kStringFromCodePoint:
result = LowerStringFromCodePoint(node); result = LowerStringFromCodePoint(node);
break; break;
case IrOpcode::kStringIndexOf:
result = LowerStringIndexOf(node);
break;
case IrOpcode::kStringCharAt: case IrOpcode::kStringCharAt:
result = LowerStringCharAt(node); result = LowerStringCharAt(node);
break; break;
...@@ -2056,6 +2059,20 @@ Node* EffectControlLinearizer::LowerStringFromCodePoint(Node* node) { ...@@ -2056,6 +2059,20 @@ Node* EffectControlLinearizer::LowerStringFromCodePoint(Node* node) {
return done.PhiAt(0); return done.PhiAt(0);
} }
Node* EffectControlLinearizer::LowerStringIndexOf(Node* node) {
Node* subject = node->InputAt(0);
Node* search_string = node->InputAt(1);
Node* position = node->InputAt(2);
Callable callable = CodeFactory::StringIndexOf(isolate());
Operator::Properties properties = Operator::kEliminatable;
CallDescriptor::Flags flags = CallDescriptor::kNoFlags;
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties);
return __ Call(desc, __ HeapConstant(callable.code()), subject, search_string,
position, __ NoContextConstant());
}
Node* EffectControlLinearizer::LowerStringComparison(Callable const& callable, Node* EffectControlLinearizer::LowerStringComparison(Callable const& callable,
Node* node) { Node* node) {
Node* lhs = node->InputAt(0); Node* lhs = node->InputAt(0);
......
...@@ -93,6 +93,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { ...@@ -93,6 +93,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerStringCharCodeAt(Node* node); Node* LowerStringCharCodeAt(Node* node);
Node* LowerStringFromCharCode(Node* node); Node* LowerStringFromCharCode(Node* node);
Node* LowerStringFromCodePoint(Node* node); Node* LowerStringFromCodePoint(Node* node);
Node* LowerStringIndexOf(Node* node);
Node* LowerStringEqual(Node* node); Node* LowerStringEqual(Node* node);
Node* LowerStringLessThan(Node* node); Node* LowerStringLessThan(Node* node);
Node* LowerStringLessThanOrEqual(Node* node); Node* LowerStringLessThanOrEqual(Node* node);
......
...@@ -820,6 +820,7 @@ bool EscapeStatusAnalysis::CheckUsesForEscape(Node* uses, Node* rep, ...@@ -820,6 +820,7 @@ bool EscapeStatusAnalysis::CheckUsesForEscape(Node* uses, Node* rep,
case IrOpcode::kPlainPrimitiveToFloat64: case IrOpcode::kPlainPrimitiveToFloat64:
case IrOpcode::kStringCharAt: case IrOpcode::kStringCharAt:
case IrOpcode::kStringCharCodeAt: case IrOpcode::kStringCharCodeAt:
case IrOpcode::kStringIndexOf:
case IrOpcode::kObjectIsCallable: case IrOpcode::kObjectIsCallable:
case IrOpcode::kObjectIsNonCallable: case IrOpcode::kObjectIsNonCallable:
case IrOpcode::kObjectIsNumber: case IrOpcode::kObjectIsNumber:
......
...@@ -1725,39 +1725,29 @@ Reduction JSBuiltinReducer::ReduceStringCharCodeAt(Node* node) { ...@@ -1725,39 +1725,29 @@ Reduction JSBuiltinReducer::ReduceStringCharCodeAt(Node* node) {
// ES6 String.prototype.indexOf(searchString [, position]) // ES6 String.prototype.indexOf(searchString [, position])
// #sec-string.prototype.indexof // #sec-string.prototype.indexof
Reduction JSBuiltinReducer::ReduceStringIndexOf(Node* node) { Reduction JSBuiltinReducer::ReduceStringIndexOf(Node* node) {
int arg_count = node->op()->ValueInputCount(); // We need at least target, receiver and search_string parameters.
if (arg_count != 3 && arg_count != 4) return NoChange(); if (node->op()->ValueInputCount() >= 3) {
Node* receiver; Node* search_string = NodeProperties::GetValueInput(node, 2);
if (!(receiver = GetStringWitness(node))) return NoChange(); Type* search_string_type = NodeProperties::GetType(search_string);
Node* search_string = NodeProperties::GetValueInput(node, 2); Node* position = (node->op()->ValueInputCount() >= 4)
if (!NodeProperties::GetType(search_string)->Is(Type::String())) { ? NodeProperties::GetValueInput(node, 3)
return NoChange(); : jsgraph()->ZeroConstant();
Type* position_type = NodeProperties::GetType(position);
if (search_string_type->Is(Type::String()) &&
position_type->Is(Type::SignedSmall())) {
if (Node* receiver = GetStringWitness(node)) {
RelaxEffectsAndControls(node);
node->ReplaceInput(0, receiver);
node->ReplaceInput(1, search_string);
node->ReplaceInput(2, position);
node->TrimInputCount(3);
NodeProperties::ChangeOp(node, simplified()->StringIndexOf());
return Changed(node);
}
}
} }
// Replace the current JSFunctionCall to String.prototype.indexOf with a return NoChange();
// simple Call to the unchecked StringIndexOf builtin.
Callable callable = CodeFactory::StringIndexOf(isolate());
const CallInterfaceDescriptor& descriptor = callable.descriptor();
CallDescriptor* desc =
Linkage::GetStubCallDescriptor(isolate(), graph()->zone(), descriptor,
descriptor.GetStackParameterCount(),
CallDescriptor::kNoFlags, Operator::kPure);
Node* stub_code = jsgraph()->HeapConstant(callable.code());
// The Call Operator doesn't require an effect nor a control input.
RelaxEffectsAndControls(node);
// Remove framestate since StringIndexOf cannot deopt.
node->RemoveInput(arg_count + 1);
// Remove the control input.
node->RemoveInput(arg_count + 2);
// Replace the JSFunction from the JSFunctionCall node with the CodeStub.
node->ReplaceInput(0, stub_code);
if (arg_count == 3) {
// Insert the missing position argument.
node->InsertInput(graph()->zone(), 3, jsgraph()->ZeroConstant());
}
const Operator* op = common()->Call(desc);
node->TrimInputCount(op->ValueInputCount());
NodeProperties::ChangeOp(node, op);
return Changed(node);
} }
Reduction JSBuiltinReducer::ReduceStringIterator(Node* node) { Reduction JSBuiltinReducer::ReduceStringIterator(Node* node) {
......
...@@ -308,6 +308,7 @@ ...@@ -308,6 +308,7 @@
V(StringCharCodeAt) \ V(StringCharCodeAt) \
V(StringFromCharCode) \ V(StringFromCharCode) \
V(StringFromCodePoint) \ V(StringFromCodePoint) \
V(StringIndexOf) \
V(CheckBounds) \ V(CheckBounds) \
V(CheckIf) \ V(CheckIf) \
V(CheckMaps) \ V(CheckMaps) \
......
...@@ -2240,6 +2240,13 @@ class RepresentationSelector { ...@@ -2240,6 +2240,13 @@ class RepresentationSelector {
MachineRepresentation::kTaggedPointer); MachineRepresentation::kTaggedPointer);
return; return;
} }
case IrOpcode::kStringIndexOf: {
ProcessInput(node, 0, UseInfo::AnyTagged());
ProcessInput(node, 1, UseInfo::AnyTagged());
ProcessInput(node, 2, UseInfo::TaggedSigned());
SetOutput(node, MachineRepresentation::kTaggedSigned);
return;
}
case IrOpcode::kCheckBounds: { case IrOpcode::kCheckBounds: {
Type* index_type = TypeOf(node->InputAt(0)); Type* index_type = TypeOf(node->InputAt(0));
......
...@@ -461,6 +461,7 @@ UnicodeEncoding UnicodeEncodingOf(const Operator* op) { ...@@ -461,6 +461,7 @@ UnicodeEncoding UnicodeEncodingOf(const Operator* op) {
V(StringCharAt, Operator::kNoProperties, 2, 1) \ V(StringCharAt, Operator::kNoProperties, 2, 1) \
V(StringCharCodeAt, Operator::kNoProperties, 2, 1) \ V(StringCharCodeAt, Operator::kNoProperties, 2, 1) \
V(StringFromCharCode, Operator::kNoProperties, 1, 0) \ V(StringFromCharCode, Operator::kNoProperties, 1, 0) \
V(StringIndexOf, Operator::kNoProperties, 3, 0) \
V(PlainPrimitiveToNumber, Operator::kNoProperties, 1, 0) \ V(PlainPrimitiveToNumber, Operator::kNoProperties, 1, 0) \
V(PlainPrimitiveToWord32, Operator::kNoProperties, 1, 0) \ V(PlainPrimitiveToWord32, Operator::kNoProperties, 1, 0) \
V(PlainPrimitiveToFloat64, Operator::kNoProperties, 1, 0) \ V(PlainPrimitiveToFloat64, Operator::kNoProperties, 1, 0) \
......
...@@ -356,6 +356,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final ...@@ -356,6 +356,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* StringCharCodeAt(); const Operator* StringCharCodeAt();
const Operator* StringFromCharCode(); const Operator* StringFromCharCode();
const Operator* StringFromCodePoint(UnicodeEncoding encoding); const Operator* StringFromCodePoint(UnicodeEncoding encoding);
const Operator* StringIndexOf();
const Operator* PlainPrimitiveToNumber(); const Operator* PlainPrimitiveToNumber();
const Operator* PlainPrimitiveToWord32(); const Operator* PlainPrimitiveToWord32();
......
...@@ -1766,6 +1766,10 @@ Type* Typer::Visitor::TypeStringFromCodePoint(Node* node) { ...@@ -1766,6 +1766,10 @@ Type* Typer::Visitor::TypeStringFromCodePoint(Node* node) {
return TypeUnaryOp(node, StringFromCodePointTyper); return TypeUnaryOp(node, StringFromCodePointTyper);
} }
Type* Typer::Visitor::TypeStringIndexOf(Node* node) {
return Type::Range(-1.0, String::kMaxLength - 1.0, zone());
}
Type* Typer::Visitor::TypeCheckBounds(Node* node) { Type* Typer::Visitor::TypeCheckBounds(Node* node) {
Type* index = Operand(node, 0); Type* index = Operand(node, 0);
Type* length = Operand(node, 1); Type* length = Operand(node, 1);
......
...@@ -927,12 +927,20 @@ void Verifier::Visitor::Check(Node* node) { ...@@ -927,12 +927,20 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 0, Type::Number()); CheckValueInputIs(node, 0, Type::Number());
CheckTypeIs(node, Type::String()); CheckTypeIs(node, Type::String());
break; break;
case IrOpcode::kReferenceEqual: { case IrOpcode::kStringIndexOf:
// (String, String, SignedSmall) -> SignedSmall
CheckValueInputIs(node, 0, Type::String());
CheckValueInputIs(node, 1, Type::String());
CheckValueInputIs(node, 2, Type::SignedSmall());
CheckTypeIs(node, Type::SignedSmall());
break;
case IrOpcode::kReferenceEqual:
// (Unique, Any) -> Boolean and // (Unique, Any) -> Boolean and
// (Any, Unique) -> Boolean // (Any, Unique) -> Boolean
CheckTypeIs(node, Type::Boolean()); CheckTypeIs(node, Type::Boolean());
break; break;
}
case IrOpcode::kObjectIsCallable: case IrOpcode::kObjectIsCallable:
case IrOpcode::kObjectIsNonCallable: case IrOpcode::kObjectIsNonCallable:
case IrOpcode::kObjectIsNumber: case IrOpcode::kObjectIsNumber:
......
// 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
function foo(s) {
s = s + '0123456789012';
return s.indexOf('0');
}
assertEquals(0, foo('0'));
assertEquals(0, foo('0'));
%OptimizeFunctionOnNextCall(foo);
assertEquals(0, foo('0'));
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