[turbofan] Lower StringCharCodeAt to a dedicated builtin.

Introduce a dedicated StringCharCodeAt builtin, that performs the core
logic of String.prototype.charCodeAt and lower the StringCharCodeAt
simplified operator to a call to this builtin rather than inlining the
full functionality into each and every TurboFan graph using it. This can
significantly reduce compile time in some cases (i.e. can easily shave
off over 50% of compile time overhead for small functions that call
String.prototype.charCodeAt).

Currently it returns the char code as TaggedSigned value, but
middle-term we should make it possible to return untagged values
from builtins.

R=yangguo@chromium.org

Review-Url: https://codereview.chromium.org/2600443002
Cr-Commit-Position: refs/heads/master@{#41912}
This commit is contained in:
bmeurer 2016-12-21 23:11:22 -08:00 committed by Commit bot
parent 381082168d
commit 86e2a19991
8 changed files with 58 additions and 263 deletions

View File

@ -480,6 +480,24 @@ void Builtins::Generate_StringCharAt(compiler::CodeAssemblerState* state) {
assembler.Return(result); assembler.Return(result);
} }
// static
void Builtins::Generate_StringCharCodeAt(compiler::CodeAssemblerState* state) {
typedef compiler::Node Node;
CodeStubAssembler assembler(state);
Node* receiver = assembler.Parameter(0);
Node* position = assembler.Parameter(1);
// Load the character code at the {position} from the {receiver}.
Node* code = assembler.StringCharCodeAt(receiver, position,
CodeStubAssembler::INTPTR_PARAMETERS);
// And return it as TaggedSigned value.
// TODO(turbofan): Allow builtins to return values untagged.
Node* result = assembler.SmiFromWord32(code);
assembler.Return(result);
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// ES6 section 21.1 String Objects // ES6 section 21.1 String Objects

View File

@ -105,6 +105,7 @@ namespace internal {
TFS(StringGreaterThan, BUILTIN, kNoExtraICState, Compare) \ TFS(StringGreaterThan, BUILTIN, kNoExtraICState, Compare) \
TFS(StringGreaterThanOrEqual, BUILTIN, kNoExtraICState, Compare) \ TFS(StringGreaterThanOrEqual, BUILTIN, kNoExtraICState, Compare) \
TFS(StringCharAt, BUILTIN, kNoExtraICState, StringCharAt) \ TFS(StringCharAt, BUILTIN, kNoExtraICState, StringCharAt) \
TFS(StringCharCodeAt, BUILTIN, kNoExtraICState, StringCharCodeAt) \
\ \
/* Interpreter */ \ /* Interpreter */ \
ASM(InterpreterEntryTrampoline) \ ASM(InterpreterEntryTrampoline) \

View File

@ -253,6 +253,7 @@ TFS_BUILTIN(NewRestParameterElements)
TFS_BUILTIN(PromiseHandleReject) TFS_BUILTIN(PromiseHandleReject)
TFS_BUILTIN(GetSuperConstructor) TFS_BUILTIN(GetSuperConstructor)
TFS_BUILTIN(StringCharAt) TFS_BUILTIN(StringCharAt)
TFS_BUILTIN(StringCharCodeAt)
#undef TFS_BUILTIN #undef TFS_BUILTIN

View File

@ -119,6 +119,7 @@ class V8_EXPORT_PRIVATE CodeFactory final {
static Callable StringAdd(Isolate* isolate, StringAddFlags flags, static Callable StringAdd(Isolate* isolate, StringAddFlags flags,
PretenureFlag pretenure_flag); PretenureFlag pretenure_flag);
static Callable StringCharAt(Isolate* isolate); static Callable StringCharAt(Isolate* isolate);
static Callable StringCharCodeAt(Isolate* isolate);
static Callable StringCompare(Isolate* isolate, Token::Value token); static Callable StringCompare(Isolate* isolate, Token::Value token);
static Callable StringEqual(Isolate* isolate); static Callable StringEqual(Isolate* isolate);
static Callable StringNotEqual(Isolate* isolate); static Callable StringNotEqual(Isolate* isolate);

View File

@ -2247,268 +2247,18 @@ EffectControlLinearizer::LowerStringCharAt(Node* node, Node* effect,
EffectControlLinearizer::ValueEffectControl EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerStringCharCodeAt(Node* node, Node* effect, EffectControlLinearizer::LowerStringCharCodeAt(Node* node, Node* effect,
Node* control) { Node* control) {
Node* subject = node->InputAt(0); Callable const callable = CodeFactory::StringCharCodeAt(isolate());
Node* index = node->InputAt(1); Operator::Properties properties = Operator::kNoThrow | Operator::kNoWrite;
CallDescriptor::Flags flags = CallDescriptor::kNoFlags;
// We may need to loop several times for ConsString/SlicedString {subject}s. CallDescriptor* desc = Linkage::GetStubCallDescriptor(
Node* loop = isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties,
graph()->NewNode(common()->Loop(4), control, control, control, control); MachineType::TaggedSigned());
Node* lsubject = node->InsertInput(graph()->zone(), 0,
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 4), jsgraph()->HeapConstant(callable.code()));
subject, subject, subject, subject, loop); node->InsertInput(graph()->zone(), 3, jsgraph()->NoContextConstant());
Node* lindex = node->InsertInput(graph()->zone(), 4, effect);
graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 4), index, NodeProperties::ChangeOp(node, common()->Call(desc));
index, index, index, loop); return ValueEffectControl(node, node, control);
Node* leffect = graph()->NewNode(common()->EffectPhi(4), effect, effect,
effect, effect, loop);
control = loop;
effect = leffect;
// Determine the instance type of {lsubject}.
Node* lsubject_map = effect =
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
lsubject, effect, control);
Node* lsubject_instance_type = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMapInstanceType()),
lsubject_map, effect, control);
// Check if {lsubject} is a SeqString.
Node* check0 = graph()->NewNode(
machine()->Word32Equal(),
graph()->NewNode(machine()->Word32And(), lsubject_instance_type,
jsgraph()->Int32Constant(kStringRepresentationMask)),
jsgraph()->Int32Constant(kSeqStringTag));
Node* branch0 = graph()->NewNode(common()->Branch(), check0, control);
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
Node* etrue0 = effect;
Node* vtrue0;
{
// Check if the {lsubject} is a TwoByteSeqString or a OneByteSeqString.
Node* check1 = graph()->NewNode(
machine()->Word32Equal(),
graph()->NewNode(machine()->Word32And(), lsubject_instance_type,
jsgraph()->Int32Constant(kStringEncodingMask)),
jsgraph()->Int32Constant(kTwoByteStringTag));
Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_true0);
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
Node* etrue1 = etrue0;
Node* vtrue1 = etrue1 =
graph()->NewNode(simplified()->LoadElement(
AccessBuilder::ForSeqTwoByteStringCharacter()),
lsubject, lindex, etrue1, if_true1);
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
Node* efalse1 = etrue0;
Node* vfalse1 = efalse1 =
graph()->NewNode(simplified()->LoadElement(
AccessBuilder::ForSeqOneByteStringCharacter()),
lsubject, lindex, efalse1, if_false1);
if_true0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
etrue0 =
graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_true0);
vtrue0 = graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
vtrue1, vfalse1, if_true0);
}
Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
Node* efalse0 = effect;
Node* vfalse0;
{
// Check if the {lsubject} is a ConsString.
Node* check1 = graph()->NewNode(
machine()->Word32Equal(),
graph()->NewNode(machine()->Word32And(), lsubject_instance_type,
jsgraph()->Int32Constant(kStringRepresentationMask)),
jsgraph()->Int32Constant(kConsStringTag));
Node* branch1 = graph()->NewNode(common()->Branch(), check1, if_false0);
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
Node* etrue1 = efalse0;
{
// Load the right hand side of the {lsubject} ConsString.
Node* lsubject_second = etrue1 = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForConsStringSecond()),
lsubject, etrue1, if_true1);
// Check whether the right hand side is the empty string (i.e. if
// this is really a flat string in a cons string). If that is not
// the case we flatten the string first.
Node* check2 = graph()->NewNode(machine()->WordEqual(), lsubject_second,
jsgraph()->EmptyStringConstant());
Node* branch2 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
check2, if_true1);
Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
Node* etrue2 = etrue1;
Node* vtrue2 = etrue2 = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForConsStringFirst()),
lsubject, etrue2, if_true2);
Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
Node* efalse2 = etrue1;
Node* vfalse2;
{
// Flatten the {lsubject} ConsString first.
Operator::Properties properties =
Operator::kNoDeopt | Operator::kNoThrow;
Runtime::FunctionId id = Runtime::kFlattenString;
CallDescriptor const* desc = Linkage::GetRuntimeCallDescriptor(
graph()->zone(), id, 1, properties, CallDescriptor::kNoFlags);
vfalse2 = efalse2 = graph()->NewNode(
common()->Call(desc), jsgraph()->CEntryStubConstant(1), lsubject,
jsgraph()->ExternalConstant(ExternalReference(id, isolate())),
jsgraph()->Int32Constant(1), jsgraph()->NoContextConstant(),
efalse2, if_false2);
}
// Retry the {loop} with the new subject.
loop->ReplaceInput(1, if_true2);
lindex->ReplaceInput(1, lindex);
leffect->ReplaceInput(1, etrue2);
lsubject->ReplaceInput(1, vtrue2);
loop->ReplaceInput(2, if_false2);
lindex->ReplaceInput(2, lindex);
leffect->ReplaceInput(2, efalse2);
lsubject->ReplaceInput(2, vfalse2);
}
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
Node* efalse1 = efalse0;
Node* vfalse1;
{
// Check if the {lsubject} is an ExternalString.
Node* check2 = graph()->NewNode(
machine()->Word32Equal(),
graph()->NewNode(machine()->Word32And(), lsubject_instance_type,
jsgraph()->Int32Constant(kStringRepresentationMask)),
jsgraph()->Int32Constant(kExternalStringTag));
Node* branch2 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
check2, if_false1);
Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
Node* etrue2 = efalse1;
Node* vtrue2;
{
// Check if the {lsubject} is a short external string.
Node* check3 = graph()->NewNode(
machine()->Word32Equal(),
graph()->NewNode(
machine()->Word32And(), lsubject_instance_type,
jsgraph()->Int32Constant(kShortExternalStringMask)),
jsgraph()->Int32Constant(0));
Node* branch3 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
check3, if_true2);
Node* if_true3 = graph()->NewNode(common()->IfTrue(), branch3);
Node* etrue3 = etrue2;
Node* vtrue3;
{
// Load the actual resource data from the {lsubject}.
Node* lsubject_resource_data = etrue3 = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForExternalStringResourceData()),
lsubject, etrue3, if_true3);
// Check if the {lsubject} is a TwoByteExternalString or a
// OneByteExternalString.
Node* check4 = graph()->NewNode(
machine()->Word32Equal(),
graph()->NewNode(machine()->Word32And(), lsubject_instance_type,
jsgraph()->Int32Constant(kStringEncodingMask)),
jsgraph()->Int32Constant(kTwoByteStringTag));
Node* branch4 =
graph()->NewNode(common()->Branch(), check4, if_true3);
Node* if_true4 = graph()->NewNode(common()->IfTrue(), branch4);
Node* etrue4 = etrue3;
Node* vtrue4 = etrue4 = graph()->NewNode(
simplified()->LoadElement(
AccessBuilder::ForExternalTwoByteStringCharacter()),
lsubject_resource_data, lindex, etrue4, if_true4);
Node* if_false4 = graph()->NewNode(common()->IfFalse(), branch4);
Node* efalse4 = etrue3;
Node* vfalse4 = efalse4 = graph()->NewNode(
simplified()->LoadElement(
AccessBuilder::ForExternalOneByteStringCharacter()),
lsubject_resource_data, lindex, efalse4, if_false4);
if_true3 = graph()->NewNode(common()->Merge(2), if_true4, if_false4);
etrue3 = graph()->NewNode(common()->EffectPhi(2), etrue4, efalse4,
if_true3);
vtrue3 =
graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
vtrue4, vfalse4, if_true3);
}
Node* if_false3 = graph()->NewNode(common()->IfFalse(), branch3);
Node* efalse3 = etrue2;
Node* vfalse3;
{
// The {lsubject} might be compressed, call the runtime.
Operator::Properties properties =
Operator::kNoDeopt | Operator::kNoThrow;
Runtime::FunctionId id = Runtime::kExternalStringGetChar;
CallDescriptor const* desc = Linkage::GetRuntimeCallDescriptor(
graph()->zone(), id, 2, properties, CallDescriptor::kNoFlags);
vfalse3 = efalse3 = graph()->NewNode(
common()->Call(desc), jsgraph()->CEntryStubConstant(1), lsubject,
ChangeInt32ToSmi(lindex),
jsgraph()->ExternalConstant(ExternalReference(id, isolate())),
jsgraph()->Int32Constant(2), jsgraph()->NoContextConstant(),
efalse3, if_false3);
vfalse3 = ChangeSmiToInt32(vfalse3);
}
if_true2 = graph()->NewNode(common()->Merge(2), if_true3, if_false3);
etrue2 =
graph()->NewNode(common()->EffectPhi(2), etrue3, efalse3, if_true2);
vtrue2 =
graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
vtrue3, vfalse3, if_true2);
}
Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
Node* efalse2 = efalse1;
{
// The {lsubject} is a SlicedString, continue with its parent.
Node* lsubject_parent = efalse2 = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForSlicedStringParent()),
lsubject, efalse2, if_false2);
Node* lsubject_offset = efalse2 = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForSlicedStringOffset()),
lsubject, efalse2, if_false2);
Node* lsubject_index = graph()->NewNode(
machine()->Int32Add(), lindex, ChangeSmiToInt32(lsubject_offset));
// Retry the {loop} with the parent subject.
loop->ReplaceInput(3, if_false2);
leffect->ReplaceInput(3, efalse2);
lindex->ReplaceInput(3, lsubject_index);
lsubject->ReplaceInput(3, lsubject_parent);
}
if_false1 = if_true2;
efalse1 = etrue2;
vfalse1 = vtrue2;
}
if_false0 = if_false1;
efalse0 = efalse1;
vfalse0 = vfalse1;
}
control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control);
Node* value =
graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2), vtrue0,
vfalse0, control);
return ValueEffectControl(value, effect, control);
} }
EffectControlLinearizer::ValueEffectControl EffectControlLinearizer::ValueEffectControl

View File

@ -2202,8 +2202,9 @@ class RepresentationSelector {
return; return;
} }
case IrOpcode::kStringCharCodeAt: { case IrOpcode::kStringCharCodeAt: {
// TODO(turbofan): Allow builtins to return untagged values.
VisitBinop(node, UseInfo::AnyTagged(), UseInfo::TruncatingWord32(), VisitBinop(node, UseInfo::AnyTagged(), UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32); MachineRepresentation::kTaggedSigned);
return; return;
} }
case IrOpcode::kStringFromCharCode: { case IrOpcode::kStringFromCharCode: {

View File

@ -198,6 +198,21 @@ void StringCharAtDescriptor::InitializePlatformSpecific(
DefaultInitializePlatformSpecific(data, kParameterCount); DefaultInitializePlatformSpecific(data, kParameterCount);
} }
void StringCharCodeAtDescriptor::InitializePlatformIndependent(
CallInterfaceDescriptorData* data) {
// kReceiver, kPosition
// TODO(turbofan): Allow builtins to return untagged values.
MachineType machine_types[] = {MachineType::AnyTagged(),
MachineType::IntPtr()};
data->InitializePlatformIndependent(arraysize(machine_types), 0,
machine_types);
}
void StringCharCodeAtDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
DefaultInitializePlatformSpecific(data, kParameterCount);
}
void StringCompareDescriptor::InitializePlatformSpecific( void StringCompareDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
Register registers[] = {LeftRegister(), RightRegister()}; Register registers[] = {LeftRegister(), RightRegister()};

View File

@ -74,6 +74,7 @@ class PlatformInterfaceDescriptor;
V(CountOp) \ V(CountOp) \
V(StringAdd) \ V(StringAdd) \
V(StringCharAt) \ V(StringCharAt) \
V(StringCharCodeAt) \
V(StringCompare) \ V(StringCompare) \
V(SubString) \ V(SubString) \
V(Keyed) \ V(Keyed) \
@ -697,6 +698,13 @@ class StringCharAtDescriptor final : public CallInterfaceDescriptor {
CallInterfaceDescriptor) CallInterfaceDescriptor)
}; };
class StringCharCodeAtDescriptor final : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kReceiver, kPosition)
DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE(StringCharCodeAtDescriptor,
CallInterfaceDescriptor)
};
class StringCompareDescriptor : public CallInterfaceDescriptor { class StringCompareDescriptor : public CallInterfaceDescriptor {
public: public:
DEFINE_PARAMETERS(kLeft, kRight) DEFINE_PARAMETERS(kLeft, kRight)