Commit f5871336 authored by caitp's avatar caitp Committed by Commit bot

Reland "[turbofan] inline %StringIteratorPrototype%.next in JSBuiltinReducer"

Reland https://codereview.chromium.org/2373983004/, reverted in
4e5a4d93.

Reason: CL is not responsible for Win32 Debug failures (see https://build.chromium.org/p/client.v8/builders/V8%20Win32%20-%20debug/builds/5031)

------------------------------------------------------------------------------

Implement the logic for StringIterator.prototype.next in the JSBuiltinReducer in order to allow inlining when the receiver is a JS_STRING_ITERATOR_TYPE map, built ontop of the SimplifiedOperators StringCharCodeAt and the newly added StringFromCodePoint.

Also introduces a new StringFromCodePoint simplified op which may be useful for other String builtins, such as String.fromCodePoint()

BUG=v8:5388
TBR=bmeurer@chromium.org, mstarzinger@chromium.org

Review-Url: https://codereview.chromium.org/2394823003
Cr-Commit-Position: refs/heads/master@{#40008}
parent 5ff4d02d
......@@ -1450,6 +1450,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
InstallFunction(string_iterator_prototype, "next", JS_OBJECT_TYPE,
JSObject::kHeaderSize, MaybeHandle<JSObject>(),
Builtins::kStringIteratorPrototypeNext);
next->shared()->set_builtin_function_id(kStringIteratorNext);
// Set the expected parameters for %StringIteratorPrototype%.next to 0 (not
// including the receiver), as required by the builtin.
......
......@@ -19,12 +19,6 @@ class StubCache;
enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
enum class UnicodeEncoding {
// Different unicode encodings in a |word32|:
UTF16, // hi 16bits -> trailing surrogate or 0, low 16bits -> lead surrogate
UTF32, // full UTF32 code unit / Unicode codepoint
};
#define HEAP_CONSTANT_LIST(V) \
V(BooleanMap, BooleanMap) \
V(empty_string, EmptyString) \
......
......@@ -548,6 +548,24 @@ FieldAccess AccessBuilder::ForJSGlobalObjectNativeContext() {
return access;
}
// static
FieldAccess AccessBuilder::ForJSStringIteratorString() {
FieldAccess access = {
kTaggedBase, JSStringIterator::kStringOffset, Handle<Name>(),
Type::String(), MachineType::TaggedPointer(), kPointerWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForJSStringIteratorIndex() {
FieldAccess access = {kTaggedBase,
JSStringIterator::kNextIndexOffset,
Handle<Name>(),
TypeCache::Get().kStringLengthType,
MachineType::TaggedSigned(),
kNoWriteBarrier};
return access;
}
// static
FieldAccess AccessBuilder::ForValue() {
......
......@@ -182,6 +182,12 @@ class AccessBuilder final : public AllStatic {
// Provides access to JSGlobalObject::native_context() field.
static FieldAccess ForJSGlobalObjectNativeContext();
// Provides access to JSStringIterator::string() field.
static FieldAccess ForJSStringIteratorString();
// Provides access to JSStringIterator::index() field.
static FieldAccess ForJSStringIteratorIndex();
// Provides access to JSValue::value() field.
static FieldAccess ForValue();
......
......@@ -719,6 +719,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kStringFromCharCode:
state = LowerStringFromCharCode(node, *effect, *control);
break;
case IrOpcode::kStringFromCodePoint:
state = LowerStringFromCodePoint(node, *effect, *control);
break;
case IrOpcode::kStringCharCodeAt:
state = LowerStringCharCodeAt(node, *effect, *control);
break;
......@@ -2546,6 +2549,199 @@ EffectControlLinearizer::LowerStringFromCharCode(Node* node, Node* effect,
return ValueEffectControl(value, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerStringFromCodePoint(Node* node, Node* effect,
Node* control) {
Node* value = node->InputAt(0);
Node* code = value;
Node* etrue0 = effect;
Node* vtrue0;
// Check if the {code} is a single code unit
Node* check0 = graph()->NewNode(machine()->Uint32LessThanOrEqual(), code,
jsgraph()->Uint32Constant(0xFFFF));
Node* branch0 =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control);
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
{
// Check if the {code} is a one byte character
Node* check1 = graph()->NewNode(
machine()->Uint32LessThanOrEqual(), code,
jsgraph()->Uint32Constant(String::kMaxOneByteCharCode));
Node* branch1 =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check1, if_true0);
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
Node* etrue1 = etrue0;
Node* vtrue1;
{
// Load the isolate wide single character string cache.
Node* cache =
jsgraph()->HeapConstant(factory()->single_character_string_cache());
// Compute the {cache} index for {code}.
Node* index =
machine()->Is32()
? code
: graph()->NewNode(machine()->ChangeUint32ToUint64(), code);
// Check if we have an entry for the {code} in the single character string
// cache already.
Node* entry = etrue1 = graph()->NewNode(
simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()),
cache, index, etrue1, if_true1);
Node* check2 = graph()->NewNode(machine()->WordEqual(), entry,
jsgraph()->UndefinedConstant());
Node* branch2 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
check2, if_true1);
Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
Node* etrue2 = etrue1;
Node* vtrue2;
{
// Allocate a new SeqOneByteString for {code}.
vtrue2 = etrue2 = graph()->NewNode(
simplified()->Allocate(NOT_TENURED),
jsgraph()->Int32Constant(SeqOneByteString::SizeFor(1)), etrue2,
if_true2);
etrue2 = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForMap()), vtrue2,
jsgraph()->HeapConstant(factory()->one_byte_string_map()), etrue2,
if_true2);
etrue2 = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForNameHashField()), vtrue2,
jsgraph()->IntPtrConstant(Name::kEmptyHashField), etrue2, if_true2);
etrue2 = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForStringLength()), vtrue2,
jsgraph()->SmiConstant(1), etrue2, if_true2);
etrue2 = graph()->NewNode(
machine()->Store(StoreRepresentation(MachineRepresentation::kWord8,
kNoWriteBarrier)),
vtrue2, jsgraph()->IntPtrConstant(SeqOneByteString::kHeaderSize -
kHeapObjectTag),
code, etrue2, if_true2);
// Remember it in the {cache}.
etrue2 = graph()->NewNode(
simplified()->StoreElement(AccessBuilder::ForFixedArrayElement()),
cache, index, vtrue2, etrue2, if_true2);
}
// Use the {entry} from the {cache}.
Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
Node* efalse2 = etrue0;
Node* vfalse2 = entry;
if_true1 = graph()->NewNode(common()->Merge(2), if_true2, if_false2);
etrue1 =
graph()->NewNode(common()->EffectPhi(2), etrue2, efalse2, if_true1);
vtrue1 =
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
vtrue2, vfalse2, if_true1);
}
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
Node* efalse1 = effect;
Node* vfalse1;
{
// Allocate a new SeqTwoByteString for {code}.
vfalse1 = efalse1 = graph()->NewNode(
simplified()->Allocate(NOT_TENURED),
jsgraph()->Int32Constant(SeqTwoByteString::SizeFor(1)), efalse1,
if_false1);
efalse1 = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForMap()), vfalse1,
jsgraph()->HeapConstant(factory()->string_map()), efalse1, if_false1);
efalse1 = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForNameHashField()), vfalse1,
jsgraph()->IntPtrConstant(Name::kEmptyHashField), efalse1, if_false1);
efalse1 = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForStringLength()), vfalse1,
jsgraph()->SmiConstant(1), efalse1, if_false1);
efalse1 = graph()->NewNode(
machine()->Store(StoreRepresentation(MachineRepresentation::kWord16,
kNoWriteBarrier)),
vfalse1, jsgraph()->IntPtrConstant(SeqTwoByteString::kHeaderSize -
kHeapObjectTag),
code, 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::kTagged, 2),
vtrue1, vfalse1, if_true0);
}
// Generate surrogate pair string
Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
Node* efalse0 = effect;
Node* vfalse0;
{
switch (UnicodeEncodingOf(node->op())) {
case UnicodeEncoding::UTF16:
break;
case UnicodeEncoding::UTF32: {
// Convert UTF32 to UTF16 code units, and store as a 32 bit word.
Node* lead_offset = jsgraph()->Int32Constant(0xD800 - (0x10000 >> 10));
// lead = (codepoint >> 10) + LEAD_OFFSET
Node* lead =
graph()->NewNode(machine()->Int32Add(),
graph()->NewNode(machine()->Word32Shr(), code,
jsgraph()->Int32Constant(10)),
lead_offset);
// trail = (codepoint & 0x3FF) + 0xDC00;
Node* trail =
graph()->NewNode(machine()->Int32Add(),
graph()->NewNode(machine()->Word32And(), code,
jsgraph()->Int32Constant(0x3FF)),
jsgraph()->Int32Constant(0xDC00));
// codpoint = (trail << 16) | lead;
code = graph()->NewNode(machine()->Word32Or(),
graph()->NewNode(machine()->Word32Shl(), trail,
jsgraph()->Int32Constant(16)),
lead);
break;
}
}
// Allocate a new SeqTwoByteString for {code}.
vfalse0 = efalse0 =
graph()->NewNode(simplified()->Allocate(NOT_TENURED),
jsgraph()->Int32Constant(SeqTwoByteString::SizeFor(2)),
efalse0, if_false0);
efalse0 = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForMap()), vfalse0,
jsgraph()->HeapConstant(factory()->string_map()), efalse0, if_false0);
efalse0 = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForNameHashField()), vfalse0,
jsgraph()->IntPtrConstant(Name::kEmptyHashField), efalse0, if_false0);
efalse0 = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForStringLength()), vfalse0,
jsgraph()->SmiConstant(2), efalse0, if_false0);
efalse0 = graph()->NewNode(
machine()->Store(StoreRepresentation(MachineRepresentation::kWord32,
kNoWriteBarrier)),
vfalse0, jsgraph()->IntPtrConstant(SeqTwoByteString::kHeaderSize -
kHeapObjectTag),
code, efalse0, if_false0);
}
control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control);
value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
vtrue0, vfalse0, control);
return ValueEffectControl(value, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerStringComparison(Callable const& callable,
Node* node, Node* effect,
......
......@@ -142,6 +142,8 @@ class EffectControlLinearizer {
Node* control);
ValueEffectControl LowerStringFromCharCode(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerStringFromCodePoint(Node* node, Node* effect,
Node* control);
ValueEffectControl LowerStringEqual(Node* node, Node* effect, Node* control);
ValueEffectControl LowerStringLessThan(Node* node, Node* effect,
Node* control);
......
......@@ -1026,6 +1026,139 @@ Reduction JSBuiltinReducer::ReduceStringCharCodeAt(Node* node) {
return NoChange();
}
Reduction JSBuiltinReducer::ReduceStringIteratorNext(Node* node) {
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* context = NodeProperties::GetContextInput(node);
if (HasInstanceTypeWitness(receiver, effect, JS_STRING_ITERATOR_TYPE)) {
Node* string = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSStringIteratorString()),
receiver, effect, control);
Node* index = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSStringIteratorIndex()),
receiver, effect, control);
Node* length = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForStringLength()), string,
effect, control);
// branch0: if (index < length)
Node* check0 =
graph()->NewNode(simplified()->NumberLessThan(), index, length);
Node* branch0 =
graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control);
Node* etrue0 = effect;
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
Node* done_true;
Node* vtrue0;
{
done_true = jsgraph()->FalseConstant();
Node* lead = graph()->NewNode(simplified()->StringCharCodeAt(), string,
index, if_true0);
// branch1: if ((lead & 0xFC00) === 0xD800)
Node* check1 = graph()->NewNode(
simplified()->NumberEqual(),
graph()->NewNode(simplified()->NumberBitwiseAnd(), lead,
jsgraph()->Int32Constant(0xFC00)),
jsgraph()->Int32Constant(0xD800));
Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
check1, if_true0);
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
Node* vtrue1;
{
Node* next_index = graph()->NewNode(simplified()->NumberAdd(), index,
jsgraph()->OneConstant());
// branch2: if ((index + 1) < length)
Node* check2 = graph()->NewNode(simplified()->NumberLessThan(),
next_index, length);
Node* branch2 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
check2, if_true1);
Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
Node* vtrue2;
{
Node* trail = graph()->NewNode(simplified()->StringCharCodeAt(),
string, next_index, if_true2);
// branch3: if ((trail & 0xFC00) === 0xDC00)
Node* check3 = graph()->NewNode(
simplified()->NumberEqual(),
graph()->NewNode(simplified()->NumberBitwiseAnd(), trail,
jsgraph()->Int32Constant(0xFC00)),
jsgraph()->Int32Constant(0xDC00));
Node* branch3 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
check3, if_true2);
Node* if_true3 = graph()->NewNode(common()->IfTrue(), branch3);
Node* vtrue3;
{
vtrue3 = graph()->NewNode(
simplified()->NumberBitwiseOr(),
graph()->NewNode(simplified()->NumberShiftLeft(), trail,
jsgraph()->Int32Constant(16)),
lead);
}
Node* if_false3 = graph()->NewNode(common()->IfFalse(), branch3);
Node* vfalse3 = lead;
if_true2 = graph()->NewNode(common()->Merge(2), if_true3, if_false3);
vtrue2 =
graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
vtrue3, vfalse3, if_true2);
}
Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
Node* vfalse2 = lead;
if_true1 = graph()->NewNode(common()->Merge(2), if_true2, if_false2);
vtrue1 =
graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
vtrue2, vfalse2, if_true1);
}
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
Node* vfalse1 = lead;
if_true0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
vtrue0 =
graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
vtrue1, vfalse1, if_true0);
vtrue0 = graph()->NewNode(
simplified()->StringFromCodePoint(UnicodeEncoding::UTF16), vtrue0);
// Update iterator.[[NextIndex]]
Node* char_length = etrue0 = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForStringLength()), vtrue0,
etrue0, if_true0);
index = graph()->NewNode(simplified()->NumberAdd(), index, char_length);
etrue0 = graph()->NewNode(
simplified()->StoreField(AccessBuilder::ForJSStringIteratorIndex()),
receiver, index, etrue0, if_true0);
}
Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
Node* done_false;
Node* vfalse0;
{
vfalse0 = jsgraph()->UndefinedConstant();
done_false = jsgraph()->TrueConstant();
}
control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
effect = graph()->NewNode(common()->EffectPhi(2), etrue0, effect, control);
Node* value =
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
vtrue0, vfalse0, control);
Node* done =
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
done_true, done_false, control);
value = effect = graph()->NewNode(javascript()->CreateIterResultObject(),
value, done, context, effect);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
return NoChange();
}
Reduction JSBuiltinReducer::ReduceArrayBufferViewAccessor(
Node* node, InstanceType instance_type, FieldAccess const& access) {
Node* receiver = NodeProperties::GetValueInput(node, 1);
......@@ -1195,6 +1328,8 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
return ReduceStringCharAt(node);
case kStringCharCodeAt:
return ReduceStringCharCodeAt(node);
case kStringIteratorNext:
return ReduceStringIteratorNext(node);
case kDataViewByteLength:
return ReduceArrayBufferViewAccessor(
node, JS_DATA_VIEW_TYPE,
......@@ -1254,6 +1389,10 @@ SimplifiedOperatorBuilder* JSBuiltinReducer::simplified() const {
return jsgraph()->simplified();
}
JSOperatorBuilder* JSBuiltinReducer::javascript() const {
return jsgraph()->javascript();
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -21,6 +21,7 @@ namespace compiler {
class CommonOperatorBuilder;
struct FieldAccess;
class JSGraph;
class JSOperatorBuilder;
class SimplifiedOperatorBuilder;
class TypeCache;
......@@ -86,6 +87,7 @@ class JSBuiltinReducer final : public AdvancedReducer {
Reduction ReduceStringCharAt(Node* node);
Reduction ReduceStringCharCodeAt(Node* node);
Reduction ReduceStringFromCharCode(Node* node);
Reduction ReduceStringIteratorNext(Node* node);
Reduction ReduceArrayBufferViewAccessor(Node* node,
InstanceType instance_type,
FieldAccess const& access);
......@@ -100,6 +102,7 @@ class JSBuiltinReducer final : public AdvancedReducer {
Isolate* isolate() const;
CommonOperatorBuilder* common() const;
SimplifiedOperatorBuilder* simplified() const;
JSOperatorBuilder* javascript() const;
CompilationDependencies* dependencies() const { return dependencies_; }
CompilationDependencies* const dependencies_;
......
......@@ -284,6 +284,7 @@
V(BooleanNot) \
V(StringCharCodeAt) \
V(StringFromCharCode) \
V(StringFromCodePoint) \
V(CheckBounds) \
V(CheckIf) \
V(CheckMaps) \
......
......@@ -2056,6 +2056,11 @@ class RepresentationSelector {
MachineRepresentation::kTagged);
return;
}
case IrOpcode::kStringFromCodePoint: {
VisitUnop(node, UseInfo::TruncatingWord32(),
MachineRepresentation::kTagged);
return;
}
case IrOpcode::kCheckBounds: {
Type* index_type = TypeOf(node->InputAt(0));
......
......@@ -336,6 +336,11 @@ PretenureFlag PretenureFlagOf(const Operator* op) {
return OpParameter<PretenureFlag>(op);
}
UnicodeEncoding UnicodeEncodingOf(const Operator* op) {
DCHECK(op->opcode() == IrOpcode::kStringFromCodePoint);
return OpParameter<UnicodeEncoding>(op);
}
#define PURE_OP_LIST(V) \
V(BooleanNot, Operator::kNoProperties, 1, 0) \
V(NumberEqual, Operator::kCommutative, 2, 0) \
......@@ -468,6 +473,17 @@ struct SimplifiedOperatorGlobalCache final {
CHECKED_OP_LIST(CHECKED)
#undef CHECKED
template <UnicodeEncoding kEncoding>
struct StringFromCodePointOperator final : public Operator {
StringFromCodePointOperator()
: Operator(IrOpcode::kStringFromCodePoint, Operator::kPure,
"StringFromCodePoint", 1, 0, 0, 1, 0, 0) {}
};
StringFromCodePointOperator<UnicodeEncoding::UTF16>
kStringFromCodePointOperatorUTF16;
StringFromCodePointOperator<UnicodeEncoding::UTF32>
kStringFromCodePointOperatorUTF32;
struct ArrayBufferWasNeuteredOperator final : public Operator {
ArrayBufferWasNeuteredOperator()
: Operator(IrOpcode::kArrayBufferWasNeutered, Operator::kEliminatable,
......@@ -754,6 +770,18 @@ const Operator* SimplifiedOperatorBuilder::StoreBuffer(BufferAccess access) {
return nullptr;
}
const Operator* SimplifiedOperatorBuilder::StringFromCodePoint(
UnicodeEncoding encoding) {
switch (encoding) {
case UnicodeEncoding::UTF16:
return &cache_.kStringFromCodePointOperatorUTF16;
case UnicodeEncoding::UTF32:
return &cache_.kStringFromCodePointOperatorUTF32;
}
UNREACHABLE();
return nullptr;
}
#define SPECULATIVE_NUMBER_BINOP(Name) \
const Operator* SimplifiedOperatorBuilder::Name(NumberOperationHint hint) { \
switch (hint) { \
......
......@@ -185,6 +185,8 @@ NumberOperationHint NumberOperationHintOf(const Operator* op)
PretenureFlag PretenureFlagOf(const Operator* op) WARN_UNUSED_RESULT;
UnicodeEncoding UnicodeEncodingOf(const Operator*) WARN_UNUSED_RESULT;
// Interface for building simplified operators, which represent the
// medium-level operations of V8, including adding numbers, allocating objects,
// indexing into objects and arrays, etc.
......@@ -289,6 +291,7 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* StringLessThanOrEqual();
const Operator* StringCharCodeAt();
const Operator* StringFromCharCode();
const Operator* StringFromCodePoint(UnicodeEncoding encoding);
const Operator* PlainPrimitiveToNumber();
const Operator* PlainPrimitiveToWord32();
......
......@@ -295,6 +295,7 @@ class Typer::Visitor : public Reducer {
static Type* ReferenceEqualTyper(Type*, Type*, Typer*);
static Type* StringFromCharCodeTyper(Type*, Typer*);
static Type* StringFromCodePointTyper(Type*, Typer*);
Reduction UpdateType(Node* node, Type* current) {
if (NodeProperties::IsTyped(node)) {
......@@ -1365,6 +1366,10 @@ Type* Typer::Visitor::JSCallFunctionTyper(Type* fun, Typer* t) {
case kStringToLowerCase:
case kStringToUpperCase:
return Type::String();
case kStringIteratorNext:
return Type::OtherObject();
// Array functions.
case kArrayIndexOf:
case kArrayLastIndexOf:
......@@ -1551,6 +1556,19 @@ Type* Typer::Visitor::StringFromCharCodeTyper(Type* type, Typer* t) {
return Type::String();
}
Type* Typer::Visitor::StringFromCodePointTyper(Type* type, Typer* t) {
type = NumberToUint32(ToNumber(type, t), t);
Factory* f = t->isolate()->factory();
double min = type->Min();
double max = type->Max();
if (min == max) {
uint32_t code = static_cast<uint32_t>(min) & String::kMaxUtf16CodeUnitU;
Handle<String> string = f->LookupSingleCharacterStringFromCode(code);
return Type::Constant(string, t->zone());
}
return Type::String();
}
Type* Typer::Visitor::TypeStringCharCodeAt(Node* node) {
// TODO(bmeurer): We could do better here based on inputs.
return Type::Range(0, kMaxUInt16, zone());
......@@ -1560,6 +1578,10 @@ Type* Typer::Visitor::TypeStringFromCharCode(Node* node) {
return TypeUnaryOp(node, StringFromCharCodeTyper);
}
Type* Typer::Visitor::TypeStringFromCodePoint(Node* node) {
return TypeUnaryOp(node, StringFromCodePointTyper);
}
Type* Typer::Visitor::TypeCheckBounds(Node* node) {
Type* index = Operand(node, 0);
Type* length = Operand(node, 1);
......
......@@ -846,6 +846,11 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 0, Type::Number());
CheckTypeIs(node, Type::String());
break;
case IrOpcode::kStringFromCodePoint:
// (Unsigned32) -> String
CheckValueInputIs(node, 0, Type::Number());
CheckTypeIs(node, Type::String());
break;
case IrOpcode::kReferenceEqual: {
// (Unique, Any) -> Boolean and
// (Any, Unique) -> Boolean
......
......@@ -1234,6 +1234,27 @@ enum LiveEditFrameDropMode {
LIVE_EDIT_CURRENTLY_SET_MODE
};
enum class UnicodeEncoding : uint8_t {
// Different unicode encodings in a |word32|:
UTF16, // hi 16bits -> trailing surrogate or 0, low 16bits -> lead surrogate
UTF32, // full UTF32 code unit / Unicode codepoint
};
inline size_t hash_value(UnicodeEncoding encoding) {
return static_cast<uint8_t>(encoding);
}
inline std::ostream& operator<<(std::ostream& os, UnicodeEncoding encoding) {
switch (encoding) {
case UnicodeEncoding::UTF16:
return os << "UTF16";
case UnicodeEncoding::UTF32:
return os << "UTF32";
}
UNREACHABLE();
return os;
}
} // namespace internal
} // namespace v8
......
......@@ -7106,6 +7106,7 @@ enum BuiltinFunctionId {
kTypedArrayByteOffset,
kTypedArrayLength,
kSharedArrayBufferByteLength,
kStringIteratorNext,
};
......
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