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) {
Node* search_string = Parameter(1);
Node* position = Parameter(2);
Label call_runtime(this);
Node* instance_type = LoadInstanceType(receiver);
Node* search_string_instance_type = LoadInstanceType(search_string);
......
......@@ -750,6 +750,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kStringFromCodePoint:
result = LowerStringFromCodePoint(node);
break;
case IrOpcode::kStringIndexOf:
result = LowerStringIndexOf(node);
break;
case IrOpcode::kStringCharAt:
result = LowerStringCharAt(node);
break;
......@@ -2056,6 +2059,20 @@ Node* EffectControlLinearizer::LowerStringFromCodePoint(Node* node) {
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* node) {
Node* lhs = node->InputAt(0);
......
......@@ -93,6 +93,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerStringCharCodeAt(Node* node);
Node* LowerStringFromCharCode(Node* node);
Node* LowerStringFromCodePoint(Node* node);
Node* LowerStringIndexOf(Node* node);
Node* LowerStringEqual(Node* node);
Node* LowerStringLessThan(Node* node);
Node* LowerStringLessThanOrEqual(Node* node);
......
......@@ -820,6 +820,7 @@ bool EscapeStatusAnalysis::CheckUsesForEscape(Node* uses, Node* rep,
case IrOpcode::kPlainPrimitiveToFloat64:
case IrOpcode::kStringCharAt:
case IrOpcode::kStringCharCodeAt:
case IrOpcode::kStringIndexOf:
case IrOpcode::kObjectIsCallable:
case IrOpcode::kObjectIsNonCallable:
case IrOpcode::kObjectIsNumber:
......
......@@ -1725,39 +1725,29 @@ Reduction JSBuiltinReducer::ReduceStringCharCodeAt(Node* node) {
// ES6 String.prototype.indexOf(searchString [, position])
// #sec-string.prototype.indexof
Reduction JSBuiltinReducer::ReduceStringIndexOf(Node* node) {
int arg_count = node->op()->ValueInputCount();
if (arg_count != 3 && arg_count != 4) return NoChange();
Node* receiver;
if (!(receiver = GetStringWitness(node))) return NoChange();
Node* search_string = NodeProperties::GetValueInput(node, 2);
if (!NodeProperties::GetType(search_string)->Is(Type::String())) {
return NoChange();
// We need at least target, receiver and search_string parameters.
if (node->op()->ValueInputCount() >= 3) {
Node* search_string = NodeProperties::GetValueInput(node, 2);
Type* search_string_type = NodeProperties::GetType(search_string);
Node* position = (node->op()->ValueInputCount() >= 4)
? NodeProperties::GetValueInput(node, 3)
: 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
// 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);
return NoChange();
}
Reduction JSBuiltinReducer::ReduceStringIterator(Node* node) {
......
......@@ -308,6 +308,7 @@
V(StringCharCodeAt) \
V(StringFromCharCode) \
V(StringFromCodePoint) \
V(StringIndexOf) \
V(CheckBounds) \
V(CheckIf) \
V(CheckMaps) \
......
......@@ -2240,6 +2240,13 @@ class RepresentationSelector {
MachineRepresentation::kTaggedPointer);
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: {
Type* index_type = TypeOf(node->InputAt(0));
......
......@@ -461,6 +461,7 @@ UnicodeEncoding UnicodeEncodingOf(const Operator* op) {
V(StringCharAt, Operator::kNoProperties, 2, 1) \
V(StringCharCodeAt, Operator::kNoProperties, 2, 1) \
V(StringFromCharCode, Operator::kNoProperties, 1, 0) \
V(StringIndexOf, Operator::kNoProperties, 3, 0) \
V(PlainPrimitiveToNumber, Operator::kNoProperties, 1, 0) \
V(PlainPrimitiveToWord32, Operator::kNoProperties, 1, 0) \
V(PlainPrimitiveToFloat64, Operator::kNoProperties, 1, 0) \
......
......@@ -356,6 +356,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* StringCharCodeAt();
const Operator* StringFromCharCode();
const Operator* StringFromCodePoint(UnicodeEncoding encoding);
const Operator* StringIndexOf();
const Operator* PlainPrimitiveToNumber();
const Operator* PlainPrimitiveToWord32();
......
......@@ -1766,6 +1766,10 @@ Type* Typer::Visitor::TypeStringFromCodePoint(Node* node) {
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* index = Operand(node, 0);
Type* length = Operand(node, 1);
......
......@@ -927,12 +927,20 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 0, Type::Number());
CheckTypeIs(node, Type::String());
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
// (Any, Unique) -> Boolean
CheckTypeIs(node, Type::Boolean());
break;
}
case IrOpcode::kObjectIsCallable:
case IrOpcode::kObjectIsNonCallable:
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