Commit 5340f767 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Add support for String.prototype.charCodeAt/charAt.

Introduce an appropriate StringCharCodeAt simplified operator and use
that to optimize the String.prototype.charCodeAt/.charAt builtins.

R=epertoso@chromium.org

Review-Url: https://codereview.chromium.org/2180373005
Cr-Commit-Position: refs/heads/master@{#38106}
parent 7a3d6c08
......@@ -386,6 +386,78 @@ FieldAccess AccessBuilder::ForStringLength() {
return access;
}
// static
FieldAccess AccessBuilder::ForConsStringFirst() {
FieldAccess access = {
kTaggedBase, ConsString::kFirstOffset, Handle<Name>(),
Type::String(), MachineType::AnyTagged(), kPointerWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForConsStringSecond() {
FieldAccess access = {
kTaggedBase, ConsString::kSecondOffset, Handle<Name>(),
Type::String(), MachineType::AnyTagged(), kPointerWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForSlicedStringOffset() {
FieldAccess access = {
kTaggedBase, SlicedString::kOffsetOffset, Handle<Name>(),
Type::SignedSmall(), MachineType::AnyTagged(), kNoWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForSlicedStringParent() {
FieldAccess access = {
kTaggedBase, SlicedString::kParentOffset, Handle<Name>(),
Type::String(), MachineType::AnyTagged(), kPointerWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForExternalStringResourceData() {
FieldAccess access = {kTaggedBase,
ExternalString::kResourceDataOffset,
Handle<Name>(),
Type::UntaggedPointer(),
MachineType::Pointer(),
kNoWriteBarrier};
return access;
}
// static
ElementAccess AccessBuilder::ForExternalOneByteStringCharacter() {
ElementAccess access = {kUntaggedBase, 0, TypeCache::Get().kUint8,
MachineType::Uint8(), kNoWriteBarrier};
return access;
}
// static
ElementAccess AccessBuilder::ForExternalTwoByteStringCharacter() {
ElementAccess access = {kUntaggedBase, 0, TypeCache::Get().kUint16,
MachineType::Uint16(), kNoWriteBarrier};
return access;
}
// static
ElementAccess AccessBuilder::ForSeqOneByteStringCharacter() {
ElementAccess access = {kTaggedBase, SeqOneByteString::kHeaderSize,
TypeCache::Get().kUint8, MachineType::Uint8(),
kNoWriteBarrier};
return access;
}
// static
ElementAccess AccessBuilder::ForSeqTwoByteStringCharacter() {
ElementAccess access = {kTaggedBase, SeqTwoByteString::kHeaderSize,
TypeCache::Get().kUint16, MachineType::Uint16(),
kNoWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForJSGlobalObjectGlobalProxy() {
......@@ -398,7 +470,6 @@ FieldAccess AccessBuilder::ForJSGlobalObjectGlobalProxy() {
return access;
}
// static
FieldAccess AccessBuilder::ForJSGlobalObjectNativeContext() {
FieldAccess access = {kTaggedBase,
......
......@@ -124,6 +124,33 @@ class AccessBuilder final : public AllStatic {
// Provides access to String::length() field.
static FieldAccess ForStringLength();
// Provides access to ConsString::first() field.
static FieldAccess ForConsStringFirst();
// Provides access to ConsString::second() field.
static FieldAccess ForConsStringSecond();
// Provides access to SlicedString::offset() field.
static FieldAccess ForSlicedStringOffset();
// Provides access to SlicedString::parent() field.
static FieldAccess ForSlicedStringParent();
// Provides access to ExternalString::resource_data() field.
static FieldAccess ForExternalStringResourceData();
// Provides access to ExternalOneByteString characters.
static ElementAccess ForExternalOneByteStringCharacter();
// Provides access to ExternalTwoByteString characters.
static ElementAccess ForExternalTwoByteStringCharacter();
// Provides access to SeqOneByteString characters.
static ElementAccess ForSeqOneByteStringCharacter();
// Provides access to SeqTwoByteString characters.
static ElementAccess ForSeqTwoByteStringCharacter();
// Provides access to JSGlobalObject::global_proxy() field.
static FieldAccess ForJSGlobalObjectGlobalProxy();
......
This diff is collapsed.
......@@ -116,6 +116,8 @@ class EffectControlLinearizer {
Node* control);
ValueEffectControl LowerObjectIsUndetectable(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerStringCharCodeAt(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerStringFromCharCode(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerCheckFloat64Hole(Node* node, Node* frame_state,
......
......@@ -3,6 +3,8 @@
// found in the LICENSE file.
#include "src/compiler/js-builtin-reducer.h"
#include "src/compiler/access-builder.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/node-properties.h"
......@@ -40,6 +42,10 @@ class JSCallReduction {
return function->shared()->builtin_function_id();
}
bool ReceiverMatches(Type* type) {
return NodeProperties::GetType(receiver())->Is(type);
}
// Determines whether the call takes zero inputs.
bool InputsMatchZero() { return GetJSCallArity() == 0; }
......@@ -66,6 +72,7 @@ class JSCallReduction {
return true;
}
Node* receiver() { return NodeProperties::GetValueInput(node_, 1); }
Node* left() { return GetJSCallInput(0); }
Node* right() { return GetJSCallInput(1); }
......@@ -534,6 +541,127 @@ Reduction JSBuiltinReducer::ReduceStringFromCharCode(Node* node) {
return NoChange();
}
namespace {
Node* GetStringReceiver(Node* node) {
Node* receiver = NodeProperties::GetValueInput(node, 1);
Type* receiver_type = NodeProperties::GetType(receiver);
Node* effect = NodeProperties::GetEffectInput(node);
if (receiver_type->Is(Type::String())) return receiver;
// Check if the {node} is dominated by a CheckString renaming for
// it's {receiver}, and if so use that renaming as {receiver} for
// the lowering below.
for (Node* dominator = effect;;) {
if (dominator->opcode() == IrOpcode::kCheckString &&
dominator->InputAt(0) == receiver) {
return dominator;
}
if (dominator->op()->EffectInputCount() != 1) {
// Didn't find any appropriate CheckString node.
return nullptr;
}
dominator = NodeProperties::GetEffectInput(dominator);
}
}
} // 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::Unsigned32())) {
if (Node* receiver = GetStringReceiver(node)) {
// Determine the {receiver} length.
Node* receiver_length = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForStringLength()), receiver,
effect, control);
// 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);
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* vtrue;
{
// Load the character from the {receiver}.
vtrue = graph()->NewNode(simplified()->StringCharCodeAt(), receiver,
index, if_true);
// Return it as single character string.
vtrue = graph()->NewNode(simplified()->StringFromCharCode(), vtrue);
}
// 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::Unsigned32())) {
if (Node* receiver = GetStringReceiver(node)) {
// Determine the {receiver} length.
Node* receiver_length = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForStringLength()), receiver,
effect, control);
// 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* vtrue = graph()->NewNode(simplified()->StringCharCodeAt(),
receiver, 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();
}
Reduction JSBuiltinReducer::Reduce(Node* node) {
Reduction reduction = NoChange();
JSCallReduction r(node);
......@@ -646,6 +774,10 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
case kStringFromCharCode:
reduction = ReduceStringFromCharCode(node);
break;
case kStringCharAt:
return ReduceStringCharAt(node);
case kStringCharCodeAt:
return ReduceStringCharCodeAt(node);
default:
break;
}
......
......@@ -63,6 +63,8 @@ class JSBuiltinReducer final : public AdvancedReducer {
Reduction ReduceMathTanh(Node* node);
Reduction ReduceMathTrunc(Node* node);
Reduction ReduceNumberParseInt(Node* node);
Reduction ReduceStringCharAt(Node* node);
Reduction ReduceStringCharCodeAt(Node* node);
Reduction ReduceStringFromCharCode(Node* node);
Node* ToNumber(Node* value);
......
......@@ -48,6 +48,10 @@ Node* JSGraph::EmptyLiteralsArrayConstant() {
HeapConstant(factory()->empty_literals_array()));
}
Node* JSGraph::EmptyStringConstant() {
return CACHED(kEmptyStringConstant, HeapConstant(factory()->empty_string()));
}
Node* JSGraph::HeapNumberMapConstant() {
return CACHED(kHeapNumberMapConstant,
HeapConstant(factory()->heap_number_map()));
......
......@@ -45,6 +45,7 @@ class JSGraph : public ZoneObject {
Node* CEntryStubConstant(int result_size);
Node* EmptyFixedArrayConstant();
Node* EmptyLiteralsArrayConstant();
Node* EmptyStringConstant();
Node* HeapNumberMapConstant();
Node* OptimizedOutConstant();
Node* StaleRegisterConstant();
......@@ -151,6 +152,7 @@ class JSGraph : public ZoneObject {
kCEntryStubConstant,
kEmptyFixedArrayConstant,
kEmptyLiteralsArrayConstant,
kEmptyStringConstant,
kHeapNumberMapConstant,
kOptimizedOutConstant,
kStaleRegisterConstant,
......
......@@ -131,7 +131,10 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
control = graph()->NewNode(common()->IfFalse(), branch);
receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch);
receiverissmi_effect = effect;
} else {
} else if (access_infos.size() != 1 ||
!access_infos[0].receiver_type()->Is(Type::String())) {
// TODO(bmeurer): We omit the Smi check here if we are going to lower to
// the CheckString below; make this less horrible and adhoc.
receiver = effect = graph()->NewNode(simplified()->CheckTaggedPointer(),
receiver, effect, control);
}
......
......@@ -655,7 +655,11 @@ Reduction JSTypedLowering::ReduceJSComparison(Node* node) {
r.OneInputCannotBe(Type::StringOrReceiver())) {
const Operator* less_than;
const Operator* less_than_or_equal;
if (hint != CompareOperationHints::kAny) {
if (r.BothInputsAre(Type::Signed32()) ||
r.BothInputsAre(Type::Unsigned32())) {
less_than = simplified()->NumberLessThan();
less_than_or_equal = simplified()->NumberLessThanOrEqual();
} else if (hint != CompareOperationHints::kAny) {
less_than = simplified()->SpeculativeNumberLessThan(hint);
less_than_or_equal = simplified()->SpeculativeNumberLessThanOrEqual(hint);
} else if (r.BothInputsAre(Type::PlainPrimitive()) ||
......
......@@ -265,6 +265,7 @@
V(NumberToInt32) \
V(NumberToUint32) \
V(NumberSilenceNaN) \
V(StringCharCodeAt) \
V(StringFromCharCode) \
V(CheckBounds) \
V(CheckIf) \
......
......@@ -822,10 +822,17 @@ class RepresentationSelector {
return MachineRepresentation::kWord32;
} else if (type->Is(Type::Boolean())) {
return MachineRepresentation::kBit;
} else if (type->Is(Type::Number())) {
return MachineRepresentation::kFloat64;
} else if (use.IsUsedAsFloat64()) {
return MachineRepresentation::kFloat64;
} else if (type->Is(
Type::Union(Type::SignedSmall(), Type::NaN(), zone()))) {
// TODO(turbofan): For Phis that return either NaN or some Smi, it's
// beneficial to not go all the way to double, unless the uses are
// double uses. For tagging that just means some potentially expensive
// allocation code; we might want to do the same for -0 as well?
return MachineRepresentation::kTagged;
} else if (type->Is(Type::Number())) {
return MachineRepresentation::kFloat64;
} else if (type->Is(Type::Internal())) {
// We mark (u)int64 as Type::Internal.
// TODO(jarin) This is a workaround for our lack of (u)int64
......@@ -1936,6 +1943,11 @@ class RepresentationSelector {
}
return;
}
case IrOpcode::kStringCharCodeAt: {
VisitBinop(node, UseInfo::AnyTagged(), UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32);
return;
}
case IrOpcode::kStringFromCharCode: {
VisitUnop(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kTagged);
......
This diff is collapsed.
......@@ -251,6 +251,7 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* StringEqual();
const Operator* StringLessThan();
const Operator* StringLessThanOrEqual();
const Operator* StringCharCodeAt();
const Operator* StringFromCharCode();
const Operator* PlainPrimitiveToNumber();
......
......@@ -1883,6 +1883,11 @@ Type* Typer::Visitor::StringFromCharCodeTyper(Type* type, Typer* t) {
return Type::String();
}
Type* Typer::Visitor::TypeStringCharCodeAt(Node* node) {
// TODO(bmeurer): We could do better here based on inputs.
return Type::Range(0, kMaxUInt16, zone());
}
Type* Typer::Visitor::TypeStringFromCharCode(Node* node) {
return TypeUnaryOp(node, StringFromCharCodeTyper);
}
......
......@@ -813,6 +813,12 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 1, Type::String());
CheckUpperIs(node, Type::Boolean());
break;
case IrOpcode::kStringCharCodeAt:
// (String, Unsigned32) -> UnsignedSmall
CheckValueInputIs(node, 0, Type::String());
CheckValueInputIs(node, 1, Type::Unsigned32());
CheckUpperIs(node, Type::UnsignedSmall());
break;
case IrOpcode::kStringFromCharCode:
// Number -> String
CheckValueInputIs(node, 0, Type::Number());
......
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