Commit 1f234d27 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[builtins] Port the String.prototype.codePointAt builtin to CSA.

We already have all the functionality available in the
CodeStubAssembler, so this is merely connecting the dots.

Drive-by-fix: Improve code generation for StringCharCodeAt
to properly mark runtime entries as deferred and just use
a single slow-path.

Bug: v8:5049
Change-Id: I76793c823b23f676e65cdb717558473edb6b91cd
Reviewed-on: https://chromium-review.googlesource.com/577533
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46774}
parent d16b45eb
...@@ -1846,6 +1846,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object, ...@@ -1846,6 +1846,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
1, true); 1, true);
SimpleInstallFunction(prototype, "charCodeAt", SimpleInstallFunction(prototype, "charCodeAt",
Builtins::kStringPrototypeCharCodeAt, 1, true); Builtins::kStringPrototypeCharCodeAt, 1, true);
SimpleInstallFunction(prototype, "codePointAt",
Builtins::kStringPrototypeCodePointAt, 1, true);
SimpleInstallFunction(prototype, "concat", Builtins::kStringPrototypeConcat, SimpleInstallFunction(prototype, "concat", Builtins::kStringPrototypeConcat,
1, false); 1, false);
SimpleInstallFunction(prototype, "endsWith", SimpleInstallFunction(prototype, "endsWith",
......
...@@ -911,6 +911,8 @@ namespace internal { ...@@ -911,6 +911,8 @@ namespace internal {
TFJ(StringPrototypeCharAt, 1, kPosition) \ TFJ(StringPrototypeCharAt, 1, kPosition) \
/* ES6 #sec-string.prototype.charcodeat */ \ /* ES6 #sec-string.prototype.charcodeat */ \
TFJ(StringPrototypeCharCodeAt, 1, kPosition) \ TFJ(StringPrototypeCharCodeAt, 1, kPosition) \
/* ES6 #sec-string.prototype.codepointat */ \
TFJ(StringPrototypeCodePointAt, 1, kPosition) \
/* ES6 #sec-string.prototype.concat */ \ /* ES6 #sec-string.prototype.concat */ \
TFJ(StringPrototypeConcat, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \ TFJ(StringPrototypeConcat, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES6 #sec-string.prototype.endswith */ \ /* ES6 #sec-string.prototype.endswith */ \
......
...@@ -706,6 +706,36 @@ TF_BUILTIN(StringPrototypeCharCodeAt, CodeStubAssembler) { ...@@ -706,6 +706,36 @@ TF_BUILTIN(StringPrototypeCharCodeAt, CodeStubAssembler) {
Return(result); Return(result);
} }
// ES6 #sec-string.prototype.codepointat
TF_BUILTIN(StringPrototypeCodePointAt, StringBuiltinsAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* receiver = Parameter(Descriptor::kReceiver);
Node* position = Parameter(Descriptor::kPosition);
// Check that {receiver} is coercible to Object and convert it to a String.
receiver = ToThisString(context, receiver, "String.prototype.codePointAt");
// Convert the {position} to a Smi and check that it's in bounds of the
// {receiver}.
Label if_inbounds(this), if_outofbounds(this, Label::kDeferred);
position =
ToInteger(context, position, CodeStubAssembler::kTruncateMinusZero);
GotoIfNot(TaggedIsSmi(position), &if_outofbounds);
Node* receiver_length = LoadObjectField(receiver, String::kLengthOffset);
Branch(SmiBelow(position, receiver_length), &if_inbounds, &if_outofbounds);
BIND(&if_inbounds);
{
Node* value = LoadSurrogatePairAt(receiver, receiver_length, position,
UnicodeEncoding::UTF32);
Node* result = SmiFromWord32(value);
Return(result);
}
BIND(&if_outofbounds);
Return(UndefinedConstant());
}
// ES6 String.prototype.concat(...args) // ES6 String.prototype.concat(...args)
// ES6 #sec-string.prototype.concat // ES6 #sec-string.prototype.concat
TF_BUILTIN(StringPrototypeConcat, CodeStubAssembler) { TF_BUILTIN(StringPrototypeConcat, CodeStubAssembler) {
...@@ -1769,7 +1799,7 @@ compiler::Node* StringBuiltinsAssembler::LoadSurrogatePairAt( ...@@ -1769,7 +1799,7 @@ compiler::Node* StringBuiltinsAssembler::LoadSurrogatePairAt(
Int32Constant(0x10000 - (0xD800 << 10) - 0xDC00); Int32Constant(0x10000 - (0xD800 << 10) - 0xDC00);
// (lead << 10) + trail + SURROGATE_OFFSET // (lead << 10) + trail + SURROGATE_OFFSET
var_result.Bind(Int32Add(WordShl(lead, Int32Constant(10)), var_result.Bind(Int32Add(Word32Shl(lead, Int32Constant(10)),
Int32Add(trail, surrogate_offset))); Int32Add(trail, surrogate_offset)));
break; break;
} }
......
...@@ -3656,57 +3656,48 @@ Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index, ...@@ -3656,57 +3656,48 @@ Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index,
CSA_ASSERT(this, IsString(string)); CSA_ASSERT(this, IsString(string));
// Translate the {index} into a Word. // Translate the {index} into a Word.
Node* const int_index = ParameterToWord(index, parameter_mode); index = ParameterToWord(index, parameter_mode);
CSA_ASSERT(this, IntPtrGreaterThanOrEqual(int_index, IntPtrConstant(0))); CSA_ASSERT(this, IntPtrGreaterThanOrEqual(index, IntPtrConstant(0)));
CSA_ASSERT(this, IntPtrLessThan(index, SmiUntag(LoadStringLength(string))));
VARIABLE(var_result, MachineRepresentation::kWord32); VARIABLE(var_result, MachineRepresentation::kWord32);
Label out(this, &var_result), runtime_generic(this), runtime_external(this); Label return_result(this), if_runtime(this, Label::kDeferred),
if_stringistwobyte(this), if_stringisonebyte(this);
ToDirectStringAssembler to_direct(state(), string); ToDirectStringAssembler to_direct(state(), string);
Node* const direct_string = to_direct.TryToDirect(&runtime_generic); to_direct.TryToDirect(&if_runtime);
Node* const offset = IntPtrAdd(int_index, to_direct.offset()); Node* const offset = IntPtrAdd(index, to_direct.offset());
Node* const instance_type = to_direct.instance_type(); Node* const instance_type = to_direct.instance_type();
Node* const string_data = to_direct.PointerToData(&runtime_external); Node* const string_data = to_direct.PointerToData(&if_runtime);
// Check if the {string} is a TwoByteSeqString or a OneByteSeqString. // Check if the {string} is a TwoByteSeqString or a OneByteSeqString.
Label if_stringistwobyte(this), if_stringisonebyte(this);
Branch(IsOneByteStringInstanceType(instance_type), &if_stringisonebyte, Branch(IsOneByteStringInstanceType(instance_type), &if_stringisonebyte,
&if_stringistwobyte); &if_stringistwobyte);
BIND(&if_stringisonebyte); BIND(&if_stringisonebyte);
{ {
var_result.Bind(Load(MachineType::Uint8(), string_data, offset)); var_result.Bind(Load(MachineType::Uint8(), string_data, offset));
Goto(&out); Goto(&return_result);
} }
BIND(&if_stringistwobyte); BIND(&if_stringistwobyte);
{ {
var_result.Bind(Load(MachineType::Uint16(), string_data, var_result.Bind(Load(MachineType::Uint16(), string_data,
WordShl(offset, IntPtrConstant(1)))); WordShl(offset, IntPtrConstant(1))));
Goto(&out); Goto(&return_result);
}
BIND(&runtime_generic);
{
Node* const smi_index = ParameterToTagged(index, parameter_mode);
Node* const result = CallRuntime(Runtime::kStringCharCodeAtRT,
NoContextConstant(), string, smi_index);
var_result.Bind(SmiToWord32(result));
Goto(&out);
} }
BIND(&runtime_external); BIND(&if_runtime);
{ {
Node* const result = Node* result = CallRuntime(Runtime::kStringCharCodeAtRT,
CallRuntime(Runtime::kExternalStringGetChar, NoContextConstant(), NoContextConstant(), string, SmiTag(index));
direct_string, SmiTag(offset));
var_result.Bind(SmiToWord32(result)); var_result.Bind(SmiToWord32(result));
Goto(&out); Goto(&return_result);
} }
BIND(&out); BIND(&return_result);
return var_result.value(); return var_result.value();
} }
......
...@@ -321,7 +321,6 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) { ...@@ -321,7 +321,6 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
V(StringEqual) \ V(StringEqual) \
V(SymbolDescriptiveString) \ V(SymbolDescriptiveString) \
V(GenerateRandomNumbers) \ V(GenerateRandomNumbers) \
V(ExternalStringGetChar) \
V(GlobalPrint) \ V(GlobalPrint) \
V(AllocateInNewSpace) \ V(AllocateInNewSpace) \
V(AllocateSeqOneByteString) \ V(AllocateSeqOneByteString) \
...@@ -597,6 +596,7 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) { ...@@ -597,6 +596,7 @@ bool BuiltinHasNoSideEffect(Builtins::Name id) {
case Builtins::kStringConstructor: case Builtins::kStringConstructor:
case Builtins::kStringPrototypeCharAt: case Builtins::kStringPrototypeCharAt:
case Builtins::kStringPrototypeCharCodeAt: case Builtins::kStringPrototypeCharCodeAt:
case Builtins::kStringPrototypeCodePointAt:
case Builtins::kStringPrototypeConcat: case Builtins::kStringPrototypeConcat:
case Builtins::kStringPrototypeEndsWith: case Builtins::kStringPrototypeEndsWith:
case Builtins::kStringPrototypeIncludes: case Builtins::kStringPrototypeIncludes:
......
...@@ -167,27 +167,6 @@ DEFINE_METHODS( ...@@ -167,27 +167,6 @@ DEFINE_METHODS(
s += s; s += s;
} }
} }
/* ES#sec-string.prototype.codepointat */
codePointAt(pos) {
CHECK_OBJECT_COERCIBLE(this, "String.prototype.codePointAt");
var string = TO_STRING(this);
var size = string.length;
pos = TO_INTEGER(pos);
if (pos < 0 || pos >= size) {
return UNDEFINED;
}
var first = %_StringCharCodeAt(string, pos);
if (first < 0xD800 || first > 0xDBFF || pos + 1 == size) {
return first;
}
var second = %_StringCharCodeAt(string, pos + 1);
if (second < 0xDC00 || second > 0xDFFF) {
return first;
}
return (first - 0xD800) * 0x400 + second + 0x2400;
}
} }
); );
......
...@@ -727,14 +727,6 @@ RUNTIME_FUNCTION(Runtime_StringCharFromCode) { ...@@ -727,14 +727,6 @@ RUNTIME_FUNCTION(Runtime_StringCharFromCode) {
return isolate->heap()->empty_string(); return isolate->heap()->empty_string();
} }
RUNTIME_FUNCTION(Runtime_ExternalStringGetChar) {
SealHandleScope shs(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_CHECKED(ExternalString, string, 0);
CONVERT_INT32_ARG_CHECKED(index, 1);
return Smi::FromInt(string->Get(index));
}
RUNTIME_FUNCTION(Runtime_StringCharCodeAt) { RUNTIME_FUNCTION(Runtime_StringCharCodeAt) {
SealHandleScope shs(isolate); SealHandleScope shs(isolate);
DCHECK_EQ(2, args.length()); DCHECK_EQ(2, args.length());
......
...@@ -535,7 +535,6 @@ namespace internal { ...@@ -535,7 +535,6 @@ namespace internal {
F(StringNotEqual, 2, 1) \ F(StringNotEqual, 2, 1) \
F(FlattenString, 1, 1) \ F(FlattenString, 1, 1) \
F(StringCharFromCode, 1, 1) \ F(StringCharFromCode, 1, 1) \
F(ExternalStringGetChar, 2, 1) \
F(StringCharCodeAt, 2, 1) F(StringCharCodeAt, 2, 1)
#define FOR_EACH_INTRINSIC_SYMBOL(F) \ #define FOR_EACH_INTRINSIC_SYMBOL(F) \
......
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