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(
&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
......
......@@ -104,6 +104,7 @@ namespace internal {
TFS(StringLessThanOrEqual, BUILTIN, kNoExtraICState, Compare) \
TFS(StringGreaterThan, BUILTIN, kNoExtraICState, Compare) \
TFS(StringGreaterThanOrEqual, BUILTIN, kNoExtraICState, Compare) \
TFS(StringCharAt, BUILTIN, kNoExtraICState, StringCharAt) \
\
/* Interpreter */ \
ASM(InterpreterEntryTrampoline) \
......
......@@ -252,6 +252,7 @@ TFS_BUILTIN(NewUnmappedArgumentsElements)
TFS_BUILTIN(NewRestParameterElements)
TFS_BUILTIN(PromiseHandleReject)
TFS_BUILTIN(GetSuperConstructor)
TFS_BUILTIN(StringCharAt)
#undef TFS_BUILTIN
......
......@@ -118,6 +118,7 @@ class V8_EXPORT_PRIVATE CodeFactory final {
static Callable StringAdd(Isolate* isolate, StringAddFlags flags,
PretenureFlag pretenure_flag);
static Callable StringCharAt(Isolate* isolate);
static Callable StringCompare(Isolate* isolate, Token::Value token);
static Callable StringEqual(Isolate* isolate);
static Callable StringNotEqual(Isolate* isolate);
......
......@@ -3011,10 +3011,11 @@ Node* CodeStubAssembler::IsJSFunction(Node* object) {
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));
// 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.
Variable var_index(this, MachineType::PointerRepresentation());
......
......@@ -669,7 +669,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// String helpers.
// 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}.
Node* StringFromCharCode(Node* code);
// Return a new string object which holds a substring containing the range
......
......@@ -745,6 +745,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kStringFromCodePoint:
state = LowerStringFromCodePoint(node, *effect, *control);
break;
case IrOpcode::kStringCharAt:
state = LowerStringCharAt(node, *effect, *control);
break;
case IrOpcode::kStringCharCodeAt:
state = LowerStringCharCodeAt(node, *effect, *control);
break;
......@@ -2225,6 +2228,22 @@ EffectControlLinearizer::LowerArrayBufferWasNeutered(Node* node, Node* effect,
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::LowerStringCharCodeAt(Node* node, Node* effect,
Node* control) {
......
......@@ -149,6 +149,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* control);
ValueEffectControl LowerArrayBufferWasNeutered(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerStringCharAt(Node* node, Node* effect, Node* control);
ValueEffectControl LowerStringCharCodeAt(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerStringFromCharCode(Node* node, Node* effect,
......
......@@ -1535,16 +1535,10 @@ Reduction JSBuiltinReducer::ReduceStringCharAt(Node* node) {
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
check, control);
// Return the character from the {receiver} as single character string.
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);
}
Node* vtrue = graph()->NewNode(simplified()->StringCharAt(), receiver,
index, if_true);
// Return the empty string otherwise.
Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
......
......@@ -573,12 +573,9 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
length, effect, control);
// Load the character from the {receiver}.
value = graph()->NewNode(simplified()->StringCharCodeAt(), receiver, index,
// Return the character from the {receiver} as single character string.
value = graph()->NewNode(simplified()->StringCharAt(), receiver, index,
control);
// Return it as a single character string.
value = graph()->NewNode(simplified()->StringFromCharCode(), value);
} else {
// Retrieve the native context from the given {node}.
// Compute element access infos for the receiver maps.
......@@ -831,12 +828,9 @@ Reduction JSNativeContextSpecialization::ReduceKeyedAccess(
index = effect = graph()->NewNode(simplified()->CheckBounds(), index,
length, effect, control);
// Load the character from the {receiver}.
value = graph()->NewNode(simplified()->StringCharCodeAt(), receiver,
index, control);
// Return it as a single character string.
value = graph()->NewNode(simplified()->StringFromCharCode(), value);
// Return the character from the {receiver} as single character string.
value = graph()->NewNode(simplified()->StringCharAt(), receiver, index,
control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
......
......@@ -299,6 +299,7 @@
V(PlainPrimitiveToWord32) \
V(PlainPrimitiveToFloat64) \
V(BooleanNot) \
V(StringCharAt) \
V(StringCharCodeAt) \
V(StringFromCharCode) \
V(StringFromCodePoint) \
......
......@@ -2196,6 +2196,11 @@ class RepresentationSelector {
return VisitBinop(node, UseInfo::AnyTagged(),
MachineRepresentation::kTaggedPointer);
}
case IrOpcode::kStringCharAt: {
VisitBinop(node, UseInfo::AnyTagged(), UseInfo::TruncatingWord32(),
MachineRepresentation::kTaggedPointer);
return;
}
case IrOpcode::kStringCharCodeAt: {
VisitBinop(node, UseInfo::AnyTagged(), UseInfo::TruncatingWord32(),
MachineRepresentation::kWord32);
......
......@@ -401,6 +401,7 @@ UnicodeEncoding UnicodeEncodingOf(const Operator* op) {
V(NumberToUint32, Operator::kNoProperties, 1, 0) \
V(NumberToUint8Clamped, Operator::kNoProperties, 1, 0) \
V(NumberSilenceNaN, Operator::kNoProperties, 1, 0) \
V(StringCharAt, Operator::kNoProperties, 2, 1) \
V(StringCharCodeAt, Operator::kNoProperties, 2, 1) \
V(StringFromCharCode, Operator::kNoProperties, 1, 0) \
V(PlainPrimitiveToNumber, Operator::kNoProperties, 1, 0) \
......
......@@ -296,6 +296,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* StringEqual();
const Operator* StringLessThan();
const Operator* StringLessThanOrEqual();
const Operator* StringCharAt();
const Operator* StringCharCodeAt();
const Operator* StringFromCharCode();
const Operator* StringFromCodePoint(UnicodeEncoding encoding);
......
......@@ -1681,6 +1681,8 @@ Type* Typer::Visitor::StringFromCodePointTyper(Type* type, Typer* t) {
return Type::String();
}
Type* Typer::Visitor::TypeStringCharAt(Node* node) { return Type::String(); }
Type* Typer::Visitor::TypeStringCharCodeAt(Node* node) {
return typer_->cache_.kUint16;
}
......
......@@ -897,6 +897,12 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 1, Type::String());
CheckTypeIs(node, Type::Boolean());
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:
// (String, Unsigned32) -> UnsignedSmall
CheckValueInputIs(node, 0, Type::String());
......
......@@ -184,6 +184,20 @@ void StoreNamedTransitionDescriptor::InitializePlatformSpecific(
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(
CallInterfaceDescriptorData* data) {
Register registers[] = {LeftRegister(), RightRegister()};
......
......@@ -73,6 +73,7 @@ class PlatformInterfaceDescriptor;
V(BinaryOpWithVector) \
V(CountOp) \
V(StringAdd) \
V(StringCharAt) \
V(StringCompare) \
V(SubString) \
V(Keyed) \
......@@ -689,6 +690,12 @@ class StringAddDescriptor : public 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 {
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