Commit b6ddadd0 authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

[turbofan] Rename StringFromCodePoint to StringFromSingleCodePoint

Also add a new fast-path for String.fromCodePoint.

R=neis@chromium.org

Bug: v8:7570, v8:7340
Change-Id: I6cd6e6fc98943588ecd646f24fcda043d4033ab0
Reviewed-on: https://chromium-review.googlesource.com/978244Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52183}
parent 3f7b6a0f
...@@ -2305,7 +2305,7 @@ TF_BUILTIN(StringIteratorPrototypeNext, StringBuiltinsAssembler) { ...@@ -2305,7 +2305,7 @@ TF_BUILTIN(StringIteratorPrototypeNext, StringBuiltinsAssembler) {
{ {
UnicodeEncoding encoding = UnicodeEncoding::UTF16; UnicodeEncoding encoding = UnicodeEncoding::UTF16;
TNode<Int32T> ch = LoadSurrogatePairAt(string, length, position, encoding); TNode<Int32T> ch = LoadSurrogatePairAt(string, length, position, encoding);
TNode<String> value = StringFromCodePoint(ch, encoding); TNode<String> value = StringFromSingleCodePoint(ch, encoding);
var_value.Bind(value); var_value.Bind(value);
TNode<IntPtrT> length = LoadStringLengthAsWord(value); TNode<IntPtrT> length = LoadStringLengthAsWord(value);
StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset, StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset,
......
...@@ -5451,8 +5451,8 @@ TNode<String> CodeStubAssembler::StringAdd(Node* context, TNode<String> left, ...@@ -5451,8 +5451,8 @@ TNode<String> CodeStubAssembler::StringAdd(Node* context, TNode<String> left,
return result.value(); return result.value();
} }
TNode<String> CodeStubAssembler::StringFromCodePoint(TNode<Int32T> codepoint, TNode<String> CodeStubAssembler::StringFromSingleCodePoint(
UnicodeEncoding encoding) { TNode<Int32T> codepoint, UnicodeEncoding encoding) {
VARIABLE(var_result, MachineRepresentation::kTagged, EmptyStringConstant()); VARIABLE(var_result, MachineRepresentation::kTagged, EmptyStringConstant());
Label if_isword16(this), if_isword32(this), return_result(this); Label if_isword16(this), if_isword32(this), return_result(this);
......
...@@ -1246,8 +1246,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { ...@@ -1246,8 +1246,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Variable* var_right, Node* right_instance_type, Variable* var_right, Node* right_instance_type,
Label* did_something); Label* did_something);
TNode<String> StringFromCodePoint(TNode<Int32T> codepoint, TNode<String> StringFromSingleCodePoint(TNode<Int32T> codepoint,
UnicodeEncoding encoding); UnicodeEncoding encoding);
// Type conversion helpers. // Type conversion helpers.
enum class BigIntHandling { kConvertToNumber, kThrow }; enum class BigIntHandling { kConvertToNumber, kThrow };
......
...@@ -827,8 +827,8 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, ...@@ -827,8 +827,8 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kStringFromSingleCharCode: case IrOpcode::kStringFromSingleCharCode:
result = LowerStringFromSingleCharCode(node); result = LowerStringFromSingleCharCode(node);
break; break;
case IrOpcode::kStringFromCodePoint: case IrOpcode::kStringFromSingleCodePoint:
result = LowerStringFromCodePoint(node); result = LowerStringFromSingleCodePoint(node);
break; break;
case IrOpcode::kStringIndexOf: case IrOpcode::kStringIndexOf:
result = LowerStringIndexOf(node); result = LowerStringIndexOf(node);
...@@ -3067,7 +3067,7 @@ Node* EffectControlLinearizer::LowerStringToUpperCaseIntl(Node* node) { ...@@ -3067,7 +3067,7 @@ Node* EffectControlLinearizer::LowerStringToUpperCaseIntl(Node* node) {
#endif // V8_INTL_SUPPORT #endif // V8_INTL_SUPPORT
Node* EffectControlLinearizer::LowerStringFromCodePoint(Node* node) { Node* EffectControlLinearizer::LowerStringFromSingleCodePoint(Node* node) {
Node* value = node->InputAt(0); Node* value = node->InputAt(0);
Node* code = value; Node* code = value;
......
...@@ -126,7 +126,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { ...@@ -126,7 +126,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerStringToLowerCaseIntl(Node* node); Node* LowerStringToLowerCaseIntl(Node* node);
Node* LowerStringToUpperCaseIntl(Node* node); Node* LowerStringToUpperCaseIntl(Node* node);
Node* LowerStringFromSingleCharCode(Node* node); Node* LowerStringFromSingleCharCode(Node* node);
Node* LowerStringFromCodePoint(Node* node); Node* LowerStringFromSingleCodePoint(Node* node);
Node* LowerStringIndexOf(Node* node); Node* LowerStringIndexOf(Node* node);
Node* LowerStringSubstring(Node* node); Node* LowerStringSubstring(Node* node);
Node* LowerStringLength(Node* node); Node* LowerStringLength(Node* node);
......
...@@ -3465,6 +3465,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node, ...@@ -3465,6 +3465,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
#endif // V8_INTL_SUPPORT #endif // V8_INTL_SUPPORT
case Builtins::kStringFromCharCode: case Builtins::kStringFromCharCode:
return ReduceStringFromCharCode(node); return ReduceStringFromCharCode(node);
case Builtins::kStringFromCodePoint:
return ReduceStringFromCodePoint(node);
case Builtins::kStringPrototypeIterator: case Builtins::kStringPrototypeIterator:
return ReduceStringPrototypeIterator(node); return ReduceStringPrototypeIterator(node);
case Builtins::kStringIteratorPrototypeNext: case Builtins::kStringIteratorPrototypeNext:
...@@ -4996,6 +4998,33 @@ Reduction JSCallReducer::ReduceStringFromCharCode(Node* node) { ...@@ -4996,6 +4998,33 @@ Reduction JSCallReducer::ReduceStringFromCharCode(Node* node) {
return NoChange(); return NoChange();
} }
// ES #sec-string.fromcodepoint
Reduction JSCallReducer::ReduceStringFromCodePoint(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
CallParameters const& p = CallParametersOf(node->op());
if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
return NoChange();
}
if (node->op()->ValueInputCount() == 3) {
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* input = NodeProperties::GetValueInput(node, 2);
input = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()),
input, effect, control);
input = effect =
graph()->NewNode(simplified()->CheckBounds(p.feedback()), input,
jsgraph()->Constant(0x10FFFF + 1), effect, control);
Node* value = graph()->NewNode(
simplified()->StringFromSingleCodePoint(UnicodeEncoding::UTF32), input);
ReplaceWithValue(node, value, effect);
return Replace(value);
}
return NoChange();
}
Reduction JSCallReducer::ReduceStringPrototypeIterator(Node* node) { Reduction JSCallReducer::ReduceStringPrototypeIterator(Node* node) {
CallParameters const& p = CallParametersOf(node->op()); CallParameters const& p = CallParametersOf(node->op());
if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
...@@ -5044,7 +5073,8 @@ Reduction JSCallReducer::ReduceStringIteratorPrototypeNext(Node* node) { ...@@ -5044,7 +5073,8 @@ Reduction JSCallReducer::ReduceStringIteratorPrototypeNext(Node* node) {
simplified()->StringCodePointAt(UnicodeEncoding::UTF16), string, simplified()->StringCodePointAt(UnicodeEncoding::UTF16), string,
index, etrue0, if_true0); index, etrue0, if_true0);
vtrue0 = graph()->NewNode( vtrue0 = graph()->NewNode(
simplified()->StringFromCodePoint(UnicodeEncoding::UTF16), codepoint); simplified()->StringFromSingleCodePoint(UnicodeEncoding::UTF16),
codepoint);
// Update iterator.[[NextIndex]] // Update iterator.[[NextIndex]]
Node* char_length = Node* char_length =
......
...@@ -121,6 +121,7 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer { ...@@ -121,6 +121,7 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
#endif // V8_INTL_SUPPORT #endif // V8_INTL_SUPPORT
Reduction ReduceStringFromCharCode(Node* node); Reduction ReduceStringFromCharCode(Node* node);
Reduction ReduceStringFromCodePoint(Node* node);
Reduction ReduceStringPrototypeIterator(Node* node); Reduction ReduceStringPrototypeIterator(Node* node);
Reduction ReduceStringIteratorPrototypeNext(Node* node); Reduction ReduceStringIteratorPrototypeNext(Node* node);
Reduction ReduceStringPrototypeConcat(Node* node, Reduction ReduceStringPrototypeConcat(Node* node,
......
...@@ -345,7 +345,7 @@ ...@@ -345,7 +345,7 @@
V(StringCharCodeAt) \ V(StringCharCodeAt) \
V(StringCodePointAt) \ V(StringCodePointAt) \
V(StringFromSingleCharCode) \ V(StringFromSingleCharCode) \
V(StringFromCodePoint) \ V(StringFromSingleCodePoint) \
V(StringIndexOf) \ V(StringIndexOf) \
V(StringLength) \ V(StringLength) \
V(StringToLowerCaseIntl) \ V(StringToLowerCaseIntl) \
......
...@@ -2364,7 +2364,7 @@ class RepresentationSelector { ...@@ -2364,7 +2364,7 @@ class RepresentationSelector {
MachineRepresentation::kTaggedPointer); MachineRepresentation::kTaggedPointer);
return; return;
} }
case IrOpcode::kStringFromCodePoint: { case IrOpcode::kStringFromSingleCodePoint: {
VisitUnop(node, UseInfo::TruncatingWord32(), VisitUnop(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kTaggedPointer); MachineRepresentation::kTaggedPointer);
return; return;
......
...@@ -550,7 +550,7 @@ Type* AllocateTypeOf(const Operator* op) { ...@@ -550,7 +550,7 @@ Type* AllocateTypeOf(const Operator* op) {
} }
UnicodeEncoding UnicodeEncodingOf(const Operator* op) { UnicodeEncoding UnicodeEncodingOf(const Operator* op) {
DCHECK(op->opcode() == IrOpcode::kStringFromCodePoint || DCHECK(op->opcode() == IrOpcode::kStringFromSingleCodePoint ||
op->opcode() == IrOpcode::kStringCodePointAt); op->opcode() == IrOpcode::kStringCodePointAt);
return OpParameter<UnicodeEncoding>(op); return OpParameter<UnicodeEncoding>(op);
} }
...@@ -836,16 +836,17 @@ struct SimplifiedOperatorGlobalCache final { ...@@ -836,16 +836,17 @@ struct SimplifiedOperatorGlobalCache final {
kStringCodePointAtOperatorUTF32; kStringCodePointAtOperatorUTF32;
template <UnicodeEncoding kEncoding> template <UnicodeEncoding kEncoding>
struct StringFromCodePointOperator final : public Operator1<UnicodeEncoding> { struct StringFromSingleCodePointOperator final
StringFromCodePointOperator() : public Operator1<UnicodeEncoding> {
: Operator1<UnicodeEncoding>(IrOpcode::kStringFromCodePoint, StringFromSingleCodePointOperator()
Operator::kPure, "StringFromCodePoint", 1, : Operator1<UnicodeEncoding>(
0, 0, 1, 0, 0, kEncoding) {} IrOpcode::kStringFromSingleCodePoint, Operator::kPure,
"StringFromSingleCodePoint", 1, 0, 0, 1, 0, 0, kEncoding) {}
}; };
StringFromCodePointOperator<UnicodeEncoding::UTF16> StringFromSingleCodePointOperator<UnicodeEncoding::UTF16>
kStringFromCodePointOperatorUTF16; kStringFromSingleCodePointOperatorUTF16;
StringFromCodePointOperator<UnicodeEncoding::UTF32> StringFromSingleCodePointOperator<UnicodeEncoding::UTF32>
kStringFromCodePointOperatorUTF32; kStringFromSingleCodePointOperatorUTF32;
struct ArrayBufferWasNeuteredOperator final : public Operator { struct ArrayBufferWasNeuteredOperator final : public Operator {
ArrayBufferWasNeuteredOperator() ArrayBufferWasNeuteredOperator()
...@@ -1450,13 +1451,13 @@ const Operator* SimplifiedOperatorBuilder::StringCodePointAt( ...@@ -1450,13 +1451,13 @@ const Operator* SimplifiedOperatorBuilder::StringCodePointAt(
UNREACHABLE(); UNREACHABLE();
} }
const Operator* SimplifiedOperatorBuilder::StringFromCodePoint( const Operator* SimplifiedOperatorBuilder::StringFromSingleCodePoint(
UnicodeEncoding encoding) { UnicodeEncoding encoding) {
switch (encoding) { switch (encoding) {
case UnicodeEncoding::UTF16: case UnicodeEncoding::UTF16:
return &cache_.kStringFromCodePointOperatorUTF16; return &cache_.kStringFromSingleCodePointOperatorUTF16;
case UnicodeEncoding::UTF32: case UnicodeEncoding::UTF32:
return &cache_.kStringFromCodePointOperatorUTF32; return &cache_.kStringFromSingleCodePointOperatorUTF32;
} }
UNREACHABLE(); UNREACHABLE();
} }
......
...@@ -560,7 +560,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final ...@@ -560,7 +560,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* StringCharCodeAt(); const Operator* StringCharCodeAt();
const Operator* StringCodePointAt(UnicodeEncoding encoding); const Operator* StringCodePointAt(UnicodeEncoding encoding);
const Operator* StringFromSingleCharCode(); const Operator* StringFromSingleCharCode();
const Operator* StringFromCodePoint(UnicodeEncoding encoding); const Operator* StringFromSingleCodePoint(UnicodeEncoding encoding);
const Operator* StringIndexOf(); const Operator* StringIndexOf();
const Operator* StringLength(); const Operator* StringLength();
const Operator* StringToLowerCaseIntl(); const Operator* StringToLowerCaseIntl();
......
...@@ -326,7 +326,7 @@ class Typer::Visitor : public Reducer { ...@@ -326,7 +326,7 @@ class Typer::Visitor : public Reducer {
static Type* ReferenceEqualTyper(Type*, Type*, Typer*); static Type* ReferenceEqualTyper(Type*, Type*, Typer*);
static Type* SameValueTyper(Type*, Type*, Typer*); static Type* SameValueTyper(Type*, Type*, Typer*);
static Type* StringFromSingleCharCodeTyper(Type*, Typer*); static Type* StringFromSingleCharCodeTyper(Type*, Typer*);
static Type* StringFromCodePointTyper(Type*, Typer*); static Type* StringFromSingleCodePointTyper(Type*, Typer*);
Reduction UpdateType(Node* node, Type* current) { Reduction UpdateType(Node* node, Type* current) {
if (NodeProperties::IsTyped(node)) { if (NodeProperties::IsTyped(node)) {
...@@ -1966,7 +1966,7 @@ Type* Typer::Visitor::StringFromSingleCharCodeTyper(Type* type, Typer* t) { ...@@ -1966,7 +1966,7 @@ Type* Typer::Visitor::StringFromSingleCharCodeTyper(Type* type, Typer* t) {
return Type::String(); return Type::String();
} }
Type* Typer::Visitor::StringFromCodePointTyper(Type* type, Typer* t) { Type* Typer::Visitor::StringFromSingleCodePointTyper(Type* type, Typer* t) {
return Type::String(); return Type::String();
} }
...@@ -1990,8 +1990,8 @@ Type* Typer::Visitor::TypeStringFromSingleCharCode(Node* node) { ...@@ -1990,8 +1990,8 @@ Type* Typer::Visitor::TypeStringFromSingleCharCode(Node* node) {
return TypeUnaryOp(node, StringFromSingleCharCodeTyper); return TypeUnaryOp(node, StringFromSingleCharCodeTyper);
} }
Type* Typer::Visitor::TypeStringFromCodePoint(Node* node) { Type* Typer::Visitor::TypeStringFromSingleCodePoint(Node* node) {
return TypeUnaryOp(node, StringFromCodePointTyper); return TypeUnaryOp(node, StringFromSingleCodePointTyper);
} }
Type* Typer::Visitor::TypeStringIndexOf(Node* node) { Type* Typer::Visitor::TypeStringIndexOf(Node* node) {
......
...@@ -1105,7 +1105,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) { ...@@ -1105,7 +1105,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckValueInputIs(node, 0, Type::Number()); CheckValueInputIs(node, 0, Type::Number());
CheckTypeIs(node, Type::String()); CheckTypeIs(node, Type::String());
break; break;
case IrOpcode::kStringFromCodePoint: case IrOpcode::kStringFromSingleCodePoint:
// (Unsigned32) -> String // (Unsigned32) -> String
CheckValueInputIs(node, 0, Type::Number()); CheckValueInputIs(node, 0, Type::Number());
CheckTypeIs(node, Type::String()); CheckTypeIs(node, Type::String());
......
...@@ -5,56 +5,72 @@ ...@@ -5,56 +5,72 @@
// Tests taken from: // Tests taken from:
// https://github.com/mathiasbynens/String.fromCodePoint // https://github.com/mathiasbynens/String.fromCodePoint
assertEquals(String.fromCodePoint.length, 1); // Flags: --allow-natives-syntax
assertEquals(String.propertyIsEnumerable("fromCodePoint"), false);
function test() {
assertEquals(String.fromCodePoint(""), "\0"); assertEquals(String.fromCodePoint.length, 1);
assertEquals(String.fromCodePoint(), ""); assertEquals(String.propertyIsEnumerable("fromCodePoint"), false);
assertEquals(String.fromCodePoint(-0), "\0");
assertEquals(String.fromCodePoint(0), "\0"); assertEquals(String.fromCodePoint(""), "\0");
assertEquals(String.fromCodePoint(0x1D306), "\uD834\uDF06"); assertEquals(String.fromCodePoint(), "");
assertEquals( assertEquals(String.fromCodePoint(-0), "\0");
assertEquals(String.fromCodePoint(0), "\0");
assertEquals(String.fromCodePoint(0x1D306), "\uD834\uDF06");
assertEquals(
String.fromCodePoint(0x1D306, 0x61, 0x1D307), String.fromCodePoint(0x1D306, 0x61, 0x1D307),
"\uD834\uDF06a\uD834\uDF07"); "\uD834\uDF06a\uD834\uDF07");
assertEquals(String.fromCodePoint(0x61, 0x62, 0x1D307), "ab\uD834\uDF07"); assertEquals(String.fromCodePoint(0x61, 0x62, 0x1D307), "ab\uD834\uDF07");
assertEquals(String.fromCodePoint(false), "\0"); assertEquals(String.fromCodePoint(false), "\0");
assertEquals(String.fromCodePoint(null), "\0"); assertEquals(String.fromCodePoint(null), "\0");
assertThrows(function() { String.fromCodePoint("_"); }, RangeError); assertThrows(function () { String.fromCodePoint("_"); }, RangeError);
assertThrows(function() { String.fromCodePoint("+Infinity"); }, RangeError); assertThrows(function () { String.fromCodePoint("+Infinity"); }, RangeError);
assertThrows(function() { String.fromCodePoint("-Infinity"); }, RangeError); assertThrows(function () { String.fromCodePoint("-Infinity"); }, RangeError);
assertThrows(function() { String.fromCodePoint(-1); }, RangeError); assertThrows(function () { String.fromCodePoint(-1); }, RangeError);
assertThrows(function() { String.fromCodePoint(0x10FFFF + 1); }, RangeError); assertThrows(function () { String.fromCodePoint(0x10FFFF + 1); }, RangeError);
assertThrows(function() { String.fromCodePoint(3.14); }, RangeError); assertThrows(function () { String.fromCodePoint(3.14); }, RangeError);
assertThrows(function() { String.fromCodePoint(3e-2); }, RangeError); assertThrows(function () { String.fromCodePoint(3e-2); }, RangeError);
assertThrows(function() { String.fromCodePoint(-Infinity); }, RangeError); assertThrows(function () { String.fromCodePoint(-Infinity); }, RangeError);
assertThrows(function() { String.fromCodePoint(+Infinity); }, RangeError); assertThrows(function () { String.fromCodePoint(+Infinity); }, RangeError);
assertThrows(function() { String.fromCodePoint(NaN); }, RangeError); assertThrows(function () { String.fromCodePoint(NaN); }, RangeError);
assertThrows(function() { String.fromCodePoint(undefined); }, RangeError); assertThrows(function () { String.fromCodePoint(undefined); }, RangeError);
assertThrows(function() { String.fromCodePoint({}); }, RangeError); assertThrows(function () { String.fromCodePoint({}); }, RangeError);
assertThrows(function() { String.fromCodePoint(/./); }, RangeError); assertThrows(function () { String.fromCodePoint(/./); }, RangeError);
assertThrows(function() { String.fromCodePoint({ assertThrows(function () {
valueOf: function() { throw Error(); } }); String.fromCodePoint({
}, Error); valueOf: function () { throw Error(); }
assertThrows(function() { String.fromCodePoint({ });
valueOf: function() { throw Error(); } }); }, Error);
}, Error); assertThrows(function () {
var tmp = 0x60; String.fromCodePoint({
assertEquals(String.fromCodePoint({ valueOf: function () { throw Error(); }
valueOf: function() { ++tmp; return tmp; } });
}), "a"); }, Error);
assertEquals(tmp, 0x61); var tmp = 0x60;
assertEquals(String.fromCodePoint({
var counter = Math.pow(2, 15) * 3 / 2; valueOf: function () { ++tmp; return tmp; }
var result = []; }), "a");
while (--counter >= 0) { assertEquals(tmp, 0x61);
result.push(0); // one code unit per symbol
var counter = Math.pow(2, 15) * 3 / 2;
var result = [];
while (--counter >= 0) {
result.push(0); // one code unit per symbol
}
String.fromCodePoint.apply(null, result); // must not throw
var counter = Math.pow(2, 15) * 3 / 2;
var result = [];
while (--counter >= 0) {
result.push(0xFFFF + 1); // two code units per symbol
}
String.fromCodePoint.apply(null, result); // must not throw
} }
String.fromCodePoint.apply(null, result); // must not throw
var counter = Math.pow(2, 15) * 3 / 2; test();
var result = []; test();
while (--counter >= 0) {
result.push(0xFFFF + 1); // two code units per symbol for (var i = 0; i < 100; ++i) {
%OptimizeFunctionOnNextCall(test);
test();
} }
String.fromCodePoint.apply(null, result); // must not throw
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