Commit 05f5ebce authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Introduce a dedicated StringCharAt operator.

Previously String element access and String.prototype.charAt were
lowered to a subgraph StringFromCharCode(StringCharCodeAt(s, k)),
however that can be fairly expensive both runtime and compile time
wise. The dedicated StringCharAt operator is implemented via a call
to a builtin that does exactly this.

R=yangguo@chromium.org

Review-Url: https://codereview.chromium.org/2599683002
Cr-Commit-Position: refs/heads/master@{#41909}
parent 74308814
...@@ -463,6 +463,23 @@ void Builtins::Generate_StringGreaterThanOrEqual( ...@@ -463,6 +463,23 @@ void Builtins::Generate_StringGreaterThanOrEqual(
&assembler, RelationalComparisonMode::kGreaterThanOrEqual); &assembler, RelationalComparisonMode::kGreaterThanOrEqual);
} }
// static
void Builtins::Generate_StringCharAt(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 the single character string with only that {code}
Node* result = assembler.StringFromCharCode(code);
assembler.Return(result);
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// ES6 section 21.1 String Objects // ES6 section 21.1 String Objects
......
...@@ -104,6 +104,7 @@ namespace internal { ...@@ -104,6 +104,7 @@ namespace internal {
TFS(StringLessThanOrEqual, BUILTIN, kNoExtraICState, Compare) \ TFS(StringLessThanOrEqual, BUILTIN, kNoExtraICState, Compare) \
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) \
\ \
/* Interpreter */ \ /* Interpreter */ \
ASM(InterpreterEntryTrampoline) \ ASM(InterpreterEntryTrampoline) \
......
...@@ -252,6 +252,7 @@ TFS_BUILTIN(NewUnmappedArgumentsElements) ...@@ -252,6 +252,7 @@ TFS_BUILTIN(NewUnmappedArgumentsElements)
TFS_BUILTIN(NewRestParameterElements) TFS_BUILTIN(NewRestParameterElements)
TFS_BUILTIN(PromiseHandleReject) TFS_BUILTIN(PromiseHandleReject)
TFS_BUILTIN(GetSuperConstructor) TFS_BUILTIN(GetSuperConstructor)
TFS_BUILTIN(StringCharAt)
#undef TFS_BUILTIN #undef TFS_BUILTIN
......
...@@ -118,6 +118,7 @@ class V8_EXPORT_PRIVATE CodeFactory final { ...@@ -118,6 +118,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 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);
......
...@@ -3011,10 +3011,11 @@ Node* CodeStubAssembler::IsJSFunction(Node* object) { ...@@ -3011,10 +3011,11 @@ Node* CodeStubAssembler::IsJSFunction(Node* object) {
return HasInstanceType(object, JS_FUNCTION_TYPE); return HasInstanceType(object, JS_FUNCTION_TYPE);
} }
Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index) { Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index,
ParameterMode parameter_mode) {
CSA_ASSERT(this, IsString(string)); CSA_ASSERT(this, IsString(string));
// Translate the {index} into a Word. // Translate the {index} into a Word.
index = SmiToWord(index); index = ParameterToWord(index, parameter_mode);
// We may need to loop in case of cons or sliced strings. // We may need to loop in case of cons or sliced strings.
Variable var_index(this, MachineType::PointerRepresentation()); Variable var_index(this, MachineType::PointerRepresentation());
......
...@@ -669,7 +669,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { ...@@ -669,7 +669,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// String helpers. // String helpers.
// Load a character from a String (might flatten a ConsString). // Load a character from a String (might flatten a ConsString).
Node* StringCharCodeAt(Node* string, Node* smi_index); Node* StringCharCodeAt(Node* string, Node* index,
ParameterMode parameter_mode = SMI_PARAMETERS);
// Return the single character string with only {code}. // Return the single character string with only {code}.
Node* StringFromCharCode(Node* code); Node* StringFromCharCode(Node* code);
// Return a new string object which holds a substring containing the range // Return a new string object which holds a substring containing the range
......
...@@ -745,6 +745,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, ...@@ -745,6 +745,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kStringFromCodePoint: case IrOpcode::kStringFromCodePoint:
state = LowerStringFromCodePoint(node, *effect, *control); state = LowerStringFromCodePoint(node, *effect, *control);
break; break;
case IrOpcode::kStringCharAt:
state = LowerStringCharAt(node, *effect, *control);
break;
case IrOpcode::kStringCharCodeAt: case IrOpcode::kStringCharCodeAt:
state = LowerStringCharCodeAt(node, *effect, *control); state = LowerStringCharCodeAt(node, *effect, *control);
break; break;
...@@ -2225,6 +2228,22 @@ EffectControlLinearizer::LowerArrayBufferWasNeutered(Node* node, Node* effect, ...@@ -2225,6 +2228,22 @@ EffectControlLinearizer::LowerArrayBufferWasNeutered(Node* node, Node* effect,
return ValueEffectControl(value, effect, control); return ValueEffectControl(value, effect, control);
} }
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerStringCharAt(Node* node, Node* effect,
Node* control) {
Callable const callable = CodeFactory::StringCharAt(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);
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 EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerStringCharCodeAt(Node* node, Node* effect, EffectControlLinearizer::LowerStringCharCodeAt(Node* node, Node* effect,
Node* control) { Node* control) {
......
...@@ -149,6 +149,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { ...@@ -149,6 +149,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* control); Node* control);
ValueEffectControl LowerArrayBufferWasNeutered(Node* node, Node* effect, ValueEffectControl LowerArrayBufferWasNeutered(Node* node, Node* effect,
Node* control); Node* control);
ValueEffectControl LowerStringCharAt(Node* node, Node* effect, Node* control);
ValueEffectControl LowerStringCharCodeAt(Node* node, Node* effect, ValueEffectControl LowerStringCharCodeAt(Node* node, Node* effect,
Node* control); Node* control);
ValueEffectControl LowerStringFromCharCode(Node* node, Node* effect, ValueEffectControl LowerStringFromCharCode(Node* node, Node* effect,
......
...@@ -1535,16 +1535,10 @@ Reduction JSBuiltinReducer::ReduceStringCharAt(Node* node) { ...@@ -1535,16 +1535,10 @@ Reduction JSBuiltinReducer::ReduceStringCharAt(Node* node) {
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
check, control); check, control);
// Return the character from the {receiver} as single character string.
Node* if_true = graph()->NewNode(common()->IfTrue(), branch); Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
Node* vtrue; Node* vtrue = graph()->NewNode(simplified()->StringCharAt(), receiver,
{ index, if_true);
// 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. // Return the empty string otherwise.
Node* if_false = graph()->NewNode(common()->IfFalse(), branch); Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
......
...@@ -573,12 +573,9 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( ...@@ -573,12 +573,9 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
index = effect = graph()->NewNode(simplified()->CheckBounds(), index, index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
length, effect, control); length, effect, control);
// Load the character from the {receiver}. // Return the character from the {receiver} as single character string.
value = graph()->NewNode(simplified()->StringCharCodeAt(), receiver, index, value = graph()->NewNode(simplified()->StringCharAt(), receiver, index,
control); control);
// Return it as a single character string.
value = graph()->NewNode(simplified()->StringFromCharCode(), value);
} else { } else {
// Retrieve the native context from the given {node}. // Retrieve the native context from the given {node}.
// Compute element access infos for the receiver maps. // Compute element access infos for the receiver maps.
...@@ -831,12 +828,9 @@ Reduction JSNativeContextSpecialization::ReduceKeyedAccess( ...@@ -831,12 +828,9 @@ Reduction JSNativeContextSpecialization::ReduceKeyedAccess(
index = effect = graph()->NewNode(simplified()->CheckBounds(), index, index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
length, effect, control); length, effect, control);
// Load the character from the {receiver}. // Return the character from the {receiver} as single character string.
value = graph()->NewNode(simplified()->StringCharCodeAt(), receiver, value = graph()->NewNode(simplified()->StringCharAt(), receiver, index,
index, control); control);
// Return it as a single character string.
value = graph()->NewNode(simplified()->StringFromCharCode(), value);
ReplaceWithValue(node, value, effect, control); ReplaceWithValue(node, value, effect, control);
return Replace(value); return Replace(value);
} }
......
...@@ -299,6 +299,7 @@ ...@@ -299,6 +299,7 @@
V(PlainPrimitiveToWord32) \ V(PlainPrimitiveToWord32) \
V(PlainPrimitiveToFloat64) \ V(PlainPrimitiveToFloat64) \
V(BooleanNot) \ V(BooleanNot) \
V(StringCharAt) \
V(StringCharCodeAt) \ V(StringCharCodeAt) \
V(StringFromCharCode) \ V(StringFromCharCode) \
V(StringFromCodePoint) \ V(StringFromCodePoint) \
......
...@@ -2196,6 +2196,11 @@ class RepresentationSelector { ...@@ -2196,6 +2196,11 @@ class RepresentationSelector {
return VisitBinop(node, UseInfo::AnyTagged(), return VisitBinop(node, UseInfo::AnyTagged(),
MachineRepresentation::kTaggedPointer); MachineRepresentation::kTaggedPointer);
} }
case IrOpcode::kStringCharAt: {
VisitBinop(node, UseInfo::AnyTagged(), UseInfo::TruncatingWord32(),
MachineRepresentation::kTaggedPointer);
return;
}
case IrOpcode::kStringCharCodeAt: { case IrOpcode::kStringCharCodeAt: {
VisitBinop(node, UseInfo::AnyTagged(), UseInfo::TruncatingWord32(), VisitBinop(node, UseInfo::AnyTagged(), UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32); MachineRepresentation::kWord32);
......
...@@ -401,6 +401,7 @@ UnicodeEncoding UnicodeEncodingOf(const Operator* op) { ...@@ -401,6 +401,7 @@ UnicodeEncoding UnicodeEncodingOf(const Operator* op) {
V(NumberToUint32, Operator::kNoProperties, 1, 0) \ V(NumberToUint32, Operator::kNoProperties, 1, 0) \
V(NumberToUint8Clamped, Operator::kNoProperties, 1, 0) \ V(NumberToUint8Clamped, Operator::kNoProperties, 1, 0) \
V(NumberSilenceNaN, Operator::kNoProperties, 1, 0) \ V(NumberSilenceNaN, Operator::kNoProperties, 1, 0) \
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(PlainPrimitiveToNumber, Operator::kNoProperties, 1, 0) \ V(PlainPrimitiveToNumber, Operator::kNoProperties, 1, 0) \
......
...@@ -296,6 +296,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final ...@@ -296,6 +296,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* StringEqual(); const Operator* StringEqual();
const Operator* StringLessThan(); const Operator* StringLessThan();
const Operator* StringLessThanOrEqual(); const Operator* StringLessThanOrEqual();
const Operator* StringCharAt();
const Operator* StringCharCodeAt(); const Operator* StringCharCodeAt();
const Operator* StringFromCharCode(); const Operator* StringFromCharCode();
const Operator* StringFromCodePoint(UnicodeEncoding encoding); const Operator* StringFromCodePoint(UnicodeEncoding encoding);
......
...@@ -1681,6 +1681,8 @@ Type* Typer::Visitor::StringFromCodePointTyper(Type* type, Typer* t) { ...@@ -1681,6 +1681,8 @@ Type* Typer::Visitor::StringFromCodePointTyper(Type* type, Typer* t) {
return Type::String(); return Type::String();
} }
Type* Typer::Visitor::TypeStringCharAt(Node* node) { return Type::String(); }
Type* Typer::Visitor::TypeStringCharCodeAt(Node* node) { Type* Typer::Visitor::TypeStringCharCodeAt(Node* node) {
return typer_->cache_.kUint16; return typer_->cache_.kUint16;
} }
......
...@@ -897,6 +897,12 @@ void Verifier::Visitor::Check(Node* node) { ...@@ -897,6 +897,12 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 1, Type::String()); CheckValueInputIs(node, 1, Type::String());
CheckTypeIs(node, Type::Boolean()); CheckTypeIs(node, Type::Boolean());
break; break;
case IrOpcode::kStringCharAt:
// (String, Unsigned32) -> String
CheckValueInputIs(node, 0, Type::String());
CheckValueInputIs(node, 1, Type::Unsigned32());
CheckTypeIs(node, Type::String());
break;
case IrOpcode::kStringCharCodeAt: case IrOpcode::kStringCharCodeAt:
// (String, Unsigned32) -> UnsignedSmall // (String, Unsigned32) -> UnsignedSmall
CheckValueInputIs(node, 0, Type::String()); CheckValueInputIs(node, 0, Type::String());
......
...@@ -184,6 +184,20 @@ void StoreNamedTransitionDescriptor::InitializePlatformSpecific( ...@@ -184,6 +184,20 @@ void StoreNamedTransitionDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(len, registers); data->InitializePlatformSpecific(len, registers);
} }
void StringCharAtDescriptor::InitializePlatformIndependent(
CallInterfaceDescriptorData* data) {
// kReceiver, kPosition
MachineType machine_types[] = {MachineType::AnyTagged(),
MachineType::IntPtr()};
data->InitializePlatformIndependent(arraysize(machine_types), 0,
machine_types);
}
void StringCharAtDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
DefaultInitializePlatformSpecific(data, kParameterCount);
}
void StringCompareDescriptor::InitializePlatformSpecific( void StringCompareDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
Register registers[] = {LeftRegister(), RightRegister()}; Register registers[] = {LeftRegister(), RightRegister()};
......
...@@ -73,6 +73,7 @@ class PlatformInterfaceDescriptor; ...@@ -73,6 +73,7 @@ class PlatformInterfaceDescriptor;
V(BinaryOpWithVector) \ V(BinaryOpWithVector) \
V(CountOp) \ V(CountOp) \
V(StringAdd) \ V(StringAdd) \
V(StringCharAt) \
V(StringCompare) \ V(StringCompare) \
V(SubString) \ V(SubString) \
V(Keyed) \ V(Keyed) \
...@@ -689,6 +690,12 @@ class StringAddDescriptor : public CallInterfaceDescriptor { ...@@ -689,6 +690,12 @@ class StringAddDescriptor : public CallInterfaceDescriptor {
DECLARE_DESCRIPTOR(StringAddDescriptor, CallInterfaceDescriptor) DECLARE_DESCRIPTOR(StringAddDescriptor, CallInterfaceDescriptor)
}; };
class StringCharAtDescriptor final : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kReceiver, kPosition)
DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE(StringCharAtDescriptor,
CallInterfaceDescriptor)
};
class StringCompareDescriptor : public CallInterfaceDescriptor { class StringCompareDescriptor : public CallInterfaceDescriptor {
public: public:
......
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