[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:
parent
381082168d
commit
86e2a19991
@ -480,6 +480,24 @@ void Builtins::Generate_StringCharAt(compiler::CodeAssemblerState* state) {
|
||||
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
|
||||
|
||||
|
@ -105,6 +105,7 @@ namespace internal {
|
||||
TFS(StringGreaterThan, BUILTIN, kNoExtraICState, Compare) \
|
||||
TFS(StringGreaterThanOrEqual, BUILTIN, kNoExtraICState, Compare) \
|
||||
TFS(StringCharAt, BUILTIN, kNoExtraICState, StringCharAt) \
|
||||
TFS(StringCharCodeAt, BUILTIN, kNoExtraICState, StringCharCodeAt) \
|
||||
\
|
||||
/* Interpreter */ \
|
||||
ASM(InterpreterEntryTrampoline) \
|
||||
|
@ -253,6 +253,7 @@ TFS_BUILTIN(NewRestParameterElements)
|
||||
TFS_BUILTIN(PromiseHandleReject)
|
||||
TFS_BUILTIN(GetSuperConstructor)
|
||||
TFS_BUILTIN(StringCharAt)
|
||||
TFS_BUILTIN(StringCharCodeAt)
|
||||
|
||||
#undef TFS_BUILTIN
|
||||
|
||||
|
@ -119,6 +119,7 @@ class V8_EXPORT_PRIVATE CodeFactory final {
|
||||
static Callable StringAdd(Isolate* isolate, StringAddFlags flags,
|
||||
PretenureFlag pretenure_flag);
|
||||
static Callable StringCharAt(Isolate* isolate);
|
||||
static Callable StringCharCodeAt(Isolate* isolate);
|
||||
static Callable StringCompare(Isolate* isolate, Token::Value token);
|
||||
static Callable StringEqual(Isolate* isolate);
|
||||
static Callable StringNotEqual(Isolate* isolate);
|
||||
|
@ -2247,268 +2247,18 @@ EffectControlLinearizer::LowerStringCharAt(Node* node, Node* effect,
|
||||
EffectControlLinearizer::ValueEffectControl
|
||||
EffectControlLinearizer::LowerStringCharCodeAt(Node* node, Node* effect,
|
||||
Node* control) {
|
||||
Node* subject = node->InputAt(0);
|
||||
Node* index = node->InputAt(1);
|
||||
|
||||
// We may need to loop several times for ConsString/SlicedString {subject}s.
|
||||
Node* loop =
|
||||
graph()->NewNode(common()->Loop(4), control, control, control, control);
|
||||
Node* lsubject =
|
||||
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 4),
|
||||
subject, subject, subject, subject, loop);
|
||||
Node* lindex =
|
||||
graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 4), index,
|
||||
index, index, index, loop);
|
||||
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);
|
||||
Callable const callable = CodeFactory::StringCharCodeAt(isolate());
|
||||
Operator::Properties properties = Operator::kNoThrow | Operator::kNoWrite;
|
||||
CallDescriptor::Flags flags = CallDescriptor::kNoFlags;
|
||||
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
|
||||
isolate(), graph()->zone(), callable.descriptor(), 0, flags, properties,
|
||||
MachineType::TaggedSigned());
|
||||
node->InsertInput(graph()->zone(), 0,
|
||||
jsgraph()->HeapConstant(callable.code()));
|
||||
node->InsertInput(graph()->zone(), 3, jsgraph()->NoContextConstant());
|
||||
node->InsertInput(graph()->zone(), 4, effect);
|
||||
NodeProperties::ChangeOp(node, common()->Call(desc));
|
||||
return ValueEffectControl(node, node, control);
|
||||
}
|
||||
|
||||
EffectControlLinearizer::ValueEffectControl
|
||||
|
@ -2202,8 +2202,9 @@ class RepresentationSelector {
|
||||
return;
|
||||
}
|
||||
case IrOpcode::kStringCharCodeAt: {
|
||||
// TODO(turbofan): Allow builtins to return untagged values.
|
||||
VisitBinop(node, UseInfo::AnyTagged(), UseInfo::TruncatingWord32(),
|
||||
MachineRepresentation::kWord32);
|
||||
MachineRepresentation::kTaggedSigned);
|
||||
return;
|
||||
}
|
||||
case IrOpcode::kStringFromCharCode: {
|
||||
|
@ -198,6 +198,21 @@ void StringCharAtDescriptor::InitializePlatformSpecific(
|
||||
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(
|
||||
CallInterfaceDescriptorData* data) {
|
||||
Register registers[] = {LeftRegister(), RightRegister()};
|
||||
|
@ -74,6 +74,7 @@ class PlatformInterfaceDescriptor;
|
||||
V(CountOp) \
|
||||
V(StringAdd) \
|
||||
V(StringCharAt) \
|
||||
V(StringCharCodeAt) \
|
||||
V(StringCompare) \
|
||||
V(SubString) \
|
||||
V(Keyed) \
|
||||
@ -697,6 +698,13 @@ class StringCharAtDescriptor final : public CallInterfaceDescriptor {
|
||||
CallInterfaceDescriptor)
|
||||
};
|
||||
|
||||
class StringCharCodeAtDescriptor final : public CallInterfaceDescriptor {
|
||||
public:
|
||||
DEFINE_PARAMETERS(kReceiver, kPosition)
|
||||
DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE(StringCharCodeAtDescriptor,
|
||||
CallInterfaceDescriptor)
|
||||
};
|
||||
|
||||
class StringCompareDescriptor : public CallInterfaceDescriptor {
|
||||
public:
|
||||
DEFINE_PARAMETERS(kLeft, kRight)
|
||||
|
Loading…
Reference in New Issue
Block a user