Reland "[builtins] migrate C++ String Iterator builtins to baseline TurboFan"

Migrate newly added C++ String Iterator builtins to TFJ builtins, per
step 4. of the String Iterator Baseline Implementation section of the design doc

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

Committed: https://crrev.com/f9a2c8b1112c4e915df8bc5f7ea1fccdf7a33ff8
Cr-Commit-Position: refs/heads/master@{#39765}

patch from issue 2358263002 at patchset 260001 (http://crrev.com/2358263002#ps260001)

Review-Url: https://codereview.chromium.org/2381053002
Cr-Commit-Position: refs/heads/master@{#39879}
This commit is contained in:
caitp 2016-09-29 08:49:08 -07:00 committed by Commit bot
parent 537c855882
commit 3c52ac79cd
7 changed files with 507 additions and 44 deletions

View File

@ -1438,9 +1438,17 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
factory->NewStringFromAsciiChecked("String Iterator"),
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY));
InstallFunction(string_iterator_prototype, "next", JS_OBJECT_TYPE,
JSObject::kHeaderSize, MaybeHandle<JSObject>(),
Builtins::kStringIteratorPrototypeNext);
Handle<JSFunction> next =
InstallFunction(string_iterator_prototype, "next", JS_OBJECT_TYPE,
JSObject::kHeaderSize, MaybeHandle<JSObject>(),
Builtins::kStringIteratorPrototypeNext);
// Set the expected parameters for %StringIteratorPrototype%.next to 0 (not
// including the receiver), as required by the builtin.
next->shared()->set_internal_formal_parameter_count(0);
// Set the length for the function to satisfy ECMA-262.
next->shared()->set_length(0);
Handle<JSFunction> string_iterator_function = CreateFunction(
isolate, factory->NewStringFromAsciiChecked("StringIterator"),

View File

@ -1028,58 +1028,443 @@ void Builtins::Generate_StringPrototypeValueOf(CodeStubAssembler* assembler) {
assembler->Return(result);
}
BUILTIN(StringPrototypeIterator) {
HandleScope scope(isolate);
TO_THIS_STRING(object, "String.prototype[Symbol.iterator]");
void Builtins::Generate_StringPrototypeIterator(CodeStubAssembler* assembler) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef CodeStubAssembler::Variable Variable;
Handle<String> string;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, string,
Object::ToString(isolate, object));
Variable var_string(assembler, MachineRepresentation::kTagged);
Variable var_index(assembler, MachineRepresentation::kTagged);
return *isolate->factory()->NewJSStringIterator(string);
Variable* loop_inputs[] = {&var_string, &var_index};
Label loop(assembler, 2, loop_inputs);
Label allocate_iterator(assembler);
Node* receiver = assembler->Parameter(0);
Node* context = assembler->Parameter(3);
var_string.Bind(assembler->ToThisString(context, receiver,
"String.prototype[Symbol.iterator]"));
var_index.Bind(assembler->SmiConstant(Smi::FromInt(0)));
assembler->Goto(&loop);
assembler->Bind(&loop);
{
Node* string = var_string.value();
// Load the instance type of the {string}.
Node* string_instance_type = assembler->LoadInstanceType(string);
// Check if the {string} is a SeqString.
Label if_stringisnotsequential(assembler);
assembler->Branch(assembler->Word32Equal(
assembler->Word32And(string_instance_type,
assembler->Int32Constant(
kStringRepresentationMask)),
assembler->Int32Constant(kSeqStringTag)),
&allocate_iterator, &if_stringisnotsequential);
assembler->Bind(&if_stringisnotsequential);
{
// Check if the {string} is a ConsString.
Label if_stringiscons(assembler), if_stringisnotcons(assembler);
assembler->Branch(
assembler->Word32Equal(
assembler->Word32And(
string_instance_type,
assembler->Int32Constant(kStringRepresentationMask)),
assembler->Int32Constant(kConsStringTag)),
&if_stringiscons, &if_stringisnotcons);
assembler->Bind(&if_stringiscons);
{
// Flatten cons-string and finish.
var_string.Bind(assembler->CallRuntime(
Runtime::kFlattenString, assembler->NoContextConstant(), string));
assembler->Goto(&allocate_iterator);
}
assembler->Bind(&if_stringisnotcons);
{
// Check if the {string} is an ExternalString.
Label if_stringisnotexternal(assembler);
assembler->Branch(
assembler->Word32Equal(
assembler->Word32And(
string_instance_type,
assembler->Int32Constant(kStringRepresentationMask)),
assembler->Int32Constant(kExternalStringTag)),
&allocate_iterator, &if_stringisnotexternal);
assembler->Bind(&if_stringisnotexternal);
{
// The {string} is a SlicedString, continue with its parent.
Node* index = var_index.value();
Node* string_offset =
assembler->LoadObjectField(string, SlicedString::kOffsetOffset);
Node* string_parent =
assembler->LoadObjectField(string, SlicedString::kParentOffset);
var_index.Bind(assembler->SmiAdd(index, string_offset));
var_string.Bind(string_parent);
assembler->Goto(&loop);
}
}
}
}
assembler->Bind(&allocate_iterator);
{
Node* native_context = assembler->LoadNativeContext(context);
Node* map = assembler->LoadFixedArrayElement(
native_context,
assembler->IntPtrConstant(Context::STRING_ITERATOR_MAP_INDEX), 0,
CodeStubAssembler::INTPTR_PARAMETERS);
Node* iterator = assembler->Allocate(JSStringIterator::kSize);
assembler->StoreMapNoWriteBarrier(iterator, map);
assembler->StoreObjectFieldRoot(iterator, JSValue::kPropertiesOffset,
Heap::kEmptyFixedArrayRootIndex);
assembler->StoreObjectFieldRoot(iterator, JSObject::kElementsOffset,
Heap::kEmptyFixedArrayRootIndex);
assembler->StoreObjectFieldNoWriteBarrier(
iterator, JSStringIterator::kStringOffset, var_string.value());
assembler->StoreObjectFieldNoWriteBarrier(
iterator, JSStringIterator::kNextIndexOffset, var_index.value());
assembler->Return(iterator);
}
}
BUILTIN(StringIteratorPrototypeNext) {
HandleScope scope(isolate);
namespace {
if (!args.receiver()->IsJSStringIterator()) {
Handle<String> reason = isolate->factory()->NewStringFromAsciiChecked(
"String Iterator.prototype.next");
THROW_NEW_ERROR_RETURN_FAILURE(
isolate,
NewTypeError(MessageTemplate::kIncompatibleMethodReceiver, reason));
// Return the |word32| codepoint at {index}. Supports SeqStrings and
// ExternalStrings.
compiler::Node* LoadSurrogatePairInternal(CodeStubAssembler* assembler,
compiler::Node* string,
compiler::Node* length,
compiler::Node* index,
UnicodeEncoding encoding) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef CodeStubAssembler::Variable Variable;
Label handle_surrogate_pair(assembler), return_result(assembler);
Variable var_result(assembler, MachineRepresentation::kWord32);
Variable var_trail(assembler, MachineRepresentation::kWord16);
var_result.Bind(assembler->Int32Constant(0));
var_trail.Bind(assembler->Int32Constant(0));
Node* string_instance_type = assembler->LoadInstanceType(string);
Label if_stringissequential(assembler), if_stringisexternal(assembler);
assembler->Branch(assembler->Word32Equal(
assembler->Word32And(string_instance_type,
assembler->Int32Constant(
kStringRepresentationMask)),
assembler->Int32Constant(kSeqStringTag)),
&if_stringissequential, &if_stringisexternal);
assembler->Bind(&if_stringissequential);
{
Label if_stringisonebyte(assembler), if_stringistwobyte(assembler);
assembler->Branch(
assembler->Word32Equal(
assembler->Word32And(string_instance_type,
assembler->Int32Constant(kStringEncodingMask)),
assembler->Int32Constant(kOneByteStringTag)),
&if_stringisonebyte, &if_stringistwobyte);
assembler->Bind(&if_stringisonebyte);
{
var_result.Bind(assembler->Load(
MachineType::Uint8(), string,
assembler->IntPtrAdd(
index, assembler->IntPtrConstant(SeqOneByteString::kHeaderSize -
kHeapObjectTag))));
assembler->Goto(&return_result);
}
assembler->Bind(&if_stringistwobyte);
{
Node* lead = assembler->Load(
MachineType::Uint16(), string,
assembler->IntPtrAdd(
assembler->WordShl(index, assembler->IntPtrConstant(1)),
assembler->IntPtrConstant(SeqTwoByteString::kHeaderSize -
kHeapObjectTag)));
var_result.Bind(lead);
Node* next_pos = assembler->Int32Add(index, assembler->Int32Constant(1));
Label if_isdoublecodeunit(assembler);
assembler->GotoIf(assembler->Int32GreaterThanOrEqual(next_pos, length),
&return_result);
assembler->GotoIf(
assembler->Uint32LessThan(lead, assembler->Int32Constant(0xD800)),
&return_result);
assembler->Branch(
assembler->Uint32LessThan(lead, assembler->Int32Constant(0xDC00)),
&if_isdoublecodeunit, &return_result);
assembler->Bind(&if_isdoublecodeunit);
{
Node* trail = assembler->Load(
MachineType::Uint16(), string,
assembler->IntPtrAdd(
assembler->WordShl(next_pos, assembler->IntPtrConstant(1)),
assembler->IntPtrConstant(SeqTwoByteString::kHeaderSize -
kHeapObjectTag)));
assembler->GotoIf(
assembler->Uint32LessThan(trail, assembler->Int32Constant(0xDC00)),
&return_result);
assembler->GotoIf(assembler->Uint32GreaterThanOrEqual(
trail, assembler->Int32Constant(0xE000)),
&return_result);
var_trail.Bind(trail);
assembler->Goto(&handle_surrogate_pair);
}
}
}
Handle<JSStringIterator> iterator =
Handle<JSStringIterator>::cast(args.receiver());
Handle<String> string(iterator->string());
int position = iterator->index();
int length = string->length();
assembler->Bind(&if_stringisexternal);
{
assembler->Assert(assembler->Word32Equal(
assembler->Word32And(
string_instance_type,
assembler->Int32Constant(kStringRepresentationMask)),
assembler->Int32Constant(kExternalStringTag)));
Label if_stringisshort(assembler), if_stringisnotshort(assembler);
if (position < length) {
uint16_t lead = string->Get(position);
if (lead >= 0xD800 && lead <= 0xDBFF && position + 1 < length) {
uint16_t trail = string->Get(position + 1);
if (V8_LIKELY(trail >= 0xDC00 && trail <= 0xDFFF)) {
// Return surrogate pair code units
iterator->set_index(position + 2);
Handle<String> value =
isolate->factory()->NewSurrogatePairString(lead, trail);
return *isolate->factory()->NewJSIteratorResult(value, false);
assembler->Branch(assembler->Word32Equal(
assembler->Word32And(string_instance_type,
assembler->Int32Constant(
kShortExternalStringMask)),
assembler->Int32Constant(0)),
&if_stringisshort, &if_stringisnotshort);
assembler->Bind(&if_stringisshort);
{
// Load the actual resource data from the {string}.
Node* string_resource_data = assembler->LoadObjectField(
string, ExternalString::kResourceDataOffset, MachineType::Pointer());
Label if_stringistwobyte(assembler), if_stringisonebyte(assembler);
assembler->Branch(assembler->Word32Equal(
assembler->Word32And(
string_instance_type,
assembler->Int32Constant(kStringEncodingMask)),
assembler->Int32Constant(kTwoByteStringTag)),
&if_stringistwobyte, &if_stringisonebyte);
assembler->Bind(&if_stringisonebyte);
{
var_result.Bind(
assembler->Load(MachineType::Uint8(), string_resource_data, index));
assembler->Goto(&return_result);
}
assembler->Bind(&if_stringistwobyte);
{
Label if_isdoublecodeunit(assembler);
Node* lead = assembler->Load(
MachineType::Uint16(), string_resource_data,
assembler->WordShl(index, assembler->IntPtrConstant(1)));
var_result.Bind(lead);
Node* next_pos =
assembler->Int32Add(index, assembler->Int32Constant(1));
assembler->GotoIf(assembler->Int32GreaterThanOrEqual(next_pos, length),
&return_result);
assembler->GotoIf(
assembler->Uint32LessThan(lead, assembler->Int32Constant(0xD800)),
&return_result);
assembler->Branch(
assembler->Uint32LessThan(lead, assembler->Int32Constant(0xDC00)),
&if_isdoublecodeunit, &return_result);
assembler->Bind(&if_isdoublecodeunit);
{
Node* trail = assembler->Load(
MachineType::Uint16(), string,
assembler->IntPtrAdd(
assembler->WordShl(next_pos, assembler->IntPtrConstant(1)),
assembler->IntPtrConstant(SeqTwoByteString::kHeaderSize -
kHeapObjectTag)));
assembler->GotoIf(assembler->Uint32LessThan(
trail, assembler->Int32Constant(0xDC00)),
&return_result);
assembler->GotoIf(assembler->Uint32GreaterThanOrEqual(
trail, assembler->Int32Constant(0xE000)),
&return_result);
var_trail.Bind(trail);
assembler->Goto(&handle_surrogate_pair);
}
}
}
// Return single code unit
iterator->set_index(position + 1);
Handle<String> value =
isolate->factory()->LookupSingleCharacterStringFromCode(lead);
return *isolate->factory()->NewJSIteratorResult(value, false);
assembler->Bind(&if_stringisnotshort);
{
Label if_isdoublecodeunit(assembler);
Node* lead = assembler->SmiToWord32(assembler->CallRuntime(
Runtime::kExternalStringGetChar, assembler->NoContextConstant(),
string, assembler->SmiTag(index)));
var_result.Bind(lead);
Node* next_pos = assembler->Int32Add(index, assembler->Int32Constant(1));
assembler->GotoIf(assembler->Int32GreaterThanOrEqual(next_pos, length),
&return_result);
assembler->GotoIf(
assembler->Uint32LessThan(lead, assembler->Int32Constant(0xD800)),
&return_result);
assembler->Branch(assembler->Uint32GreaterThanOrEqual(
lead, assembler->Int32Constant(0xDC00)),
&return_result, &if_isdoublecodeunit);
assembler->Bind(&if_isdoublecodeunit);
{
Node* trail = assembler->SmiToWord32(assembler->CallRuntime(
Runtime::kExternalStringGetChar, assembler->NoContextConstant(),
string, assembler->SmiTag(next_pos)));
assembler->GotoIf(
assembler->Uint32LessThan(trail, assembler->Int32Constant(0xDC00)),
&return_result);
assembler->GotoIf(assembler->Uint32GreaterThanOrEqual(
trail, assembler->Int32Constant(0xE000)),
&return_result);
var_trail.Bind(trail);
assembler->Goto(&handle_surrogate_pair);
}
}
}
iterator->set_string(isolate->heap()->empty_string());
assembler->Bind(&handle_surrogate_pair);
{
Node* lead = var_result.value();
Node* trail = var_trail.value();
#ifdef ENABLE_SLOW_DCHECKS
// Check that this path is only taken if a surrogate pair is found
assembler->Assert(assembler->Uint32GreaterThanOrEqual(
lead, assembler->Int32Constant(0xD800)));
assembler->Assert(
assembler->Uint32LessThan(lead, assembler->Int32Constant(0xDC00)));
assembler->Assert(assembler->Uint32GreaterThanOrEqual(
trail, assembler->Int32Constant(0xDC00)));
assembler->Assert(
assembler->Uint32LessThan(trail, assembler->Int32Constant(0xE000)));
#endif
return *isolate->factory()->NewJSIteratorResult(
isolate->factory()->undefined_value(), true);
switch (encoding) {
case UnicodeEncoding::UTF16:
var_result.Bind(assembler->WordOr(
assembler->WordShl(trail, assembler->Int32Constant(16)), lead));
break;
case UnicodeEncoding::UTF32: {
// Convert UTF16 surrogate pair into |word32| code point, encoded as
// UTF32.
Node* surrogate_offset =
assembler->Int32Constant(0x10000 - (0xD800 << 10) - 0xDC00);
// (lead << 10) + trail + SURROGATE_OFFSET
var_result.Bind(assembler->Int32Add(
assembler->WordShl(lead, assembler->Int32Constant(10)),
assembler->Int32Add(trail, surrogate_offset)));
break;
}
}
assembler->Goto(&return_result);
}
assembler->Bind(&return_result);
return var_result.value();
}
compiler::Node* LoadSurrogatePairAt(CodeStubAssembler* assembler,
compiler::Node* string,
compiler::Node* length,
compiler::Node* index) {
return LoadSurrogatePairInternal(assembler, string, length, index,
UnicodeEncoding::UTF16);
}
} // namespace
void Builtins::Generate_StringIteratorPrototypeNext(
CodeStubAssembler* assembler) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef CodeStubAssembler::Variable Variable;
Variable var_value(assembler, MachineRepresentation::kTagged);
Variable var_done(assembler, MachineRepresentation::kTagged);
var_value.Bind(assembler->UndefinedConstant());
var_done.Bind(assembler->BooleanConstant(true));
Label throw_bad_receiver(assembler), next_codepoint(assembler),
return_result(assembler);
Node* iterator = assembler->Parameter(0);
Node* context = assembler->Parameter(3);
assembler->GotoIf(assembler->WordIsSmi(iterator), &throw_bad_receiver);
assembler->GotoUnless(
assembler->WordEqual(assembler->LoadInstanceType(iterator),
assembler->Int32Constant(JS_STRING_ITERATOR_TYPE)),
&throw_bad_receiver);
Node* string =
assembler->LoadObjectField(iterator, JSStringIterator::kStringOffset);
Node* position =
assembler->LoadObjectField(iterator, JSStringIterator::kNextIndexOffset);
Node* length = assembler->LoadObjectField(string, String::kLengthOffset);
assembler->Branch(assembler->SmiLessThan(position, length), &next_codepoint,
&return_result);
assembler->Bind(&next_codepoint);
{
Node* ch =
LoadSurrogatePairAt(assembler, string, assembler->SmiUntag(length),
assembler->SmiUntag(position));
Node* value = assembler->StringFromCodePoint(ch, UnicodeEncoding::UTF16);
var_value.Bind(value);
Node* length = assembler->LoadObjectField(value, String::kLengthOffset);
assembler->StoreObjectFieldNoWriteBarrier(
iterator, JSStringIterator::kNextIndexOffset,
assembler->SmiAdd(position, length));
var_done.Bind(assembler->BooleanConstant(false));
assembler->Goto(&return_result);
}
assembler->Bind(&return_result);
{
Node* native_context = assembler->LoadNativeContext(context);
Node* map = assembler->LoadFixedArrayElement(
native_context,
assembler->IntPtrConstant(Context::ITERATOR_RESULT_MAP_INDEX), 0,
CodeStubAssembler::INTPTR_PARAMETERS);
Node* result = assembler->Allocate(JSIteratorResult::kSize);
assembler->StoreMapNoWriteBarrier(result, map);
assembler->StoreObjectFieldRoot(result, JSIteratorResult::kPropertiesOffset,
Heap::kEmptyFixedArrayRootIndex);
assembler->StoreObjectFieldRoot(result, JSIteratorResult::kElementsOffset,
Heap::kEmptyFixedArrayRootIndex);
assembler->StoreObjectFieldNoWriteBarrier(
result, JSIteratorResult::kValueOffset, var_value.value());
assembler->StoreObjectFieldNoWriteBarrier(
result, JSIteratorResult::kDoneOffset, var_done.value());
assembler->Return(result);
}
assembler->Bind(&throw_bad_receiver);
{
// The {receiver} is not a valid JSGeneratorObject.
Node* result = assembler->CallRuntime(
Runtime::kThrowIncompatibleMethodReceiver, context,
assembler->HeapConstant(assembler->factory()->NewStringFromAsciiChecked(
"String Iterator.prototype.next", TENURED)),
iterator);
assembler->Return(result); // Never reached.
}
}
} // namespace internal

View File

@ -564,10 +564,10 @@ namespace internal {
/* ES6 section 21.1.3.28 String.prototype.valueOf () */ \
TFJ(StringPrototypeValueOf, 1) \
/* ES6 #sec-string.prototype-@@iterator */ \
CPP(StringPrototypeIterator) \
TFJ(StringPrototypeIterator, 1) \
\
/* StringIterator */ \
CPP(StringIteratorPrototypeNext) \
TFJ(StringIteratorPrototypeNext, 1) \
\
/* Symbol */ \
CPP(SymbolConstructor) \

View File

@ -2734,6 +2734,58 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
return var_result.value();
}
Node* CodeStubAssembler::StringFromCodePoint(compiler::Node* codepoint,
UnicodeEncoding encoding) {
Variable var_result(this, MachineRepresentation::kTagged);
var_result.Bind(EmptyStringConstant());
Label if_isword16(this), if_isword32(this), return_result(this);
Branch(Uint32LessThan(codepoint, Int32Constant(0x10000)), &if_isword16,
&if_isword32);
Bind(&if_isword16);
{
var_result.Bind(StringFromCharCode(codepoint));
Goto(&return_result);
}
Bind(&if_isword32);
{
switch (encoding) {
case UnicodeEncoding::UTF16:
break;
case UnicodeEncoding::UTF32: {
// Convert UTF32 to UTF16 code units, and store as a 32 bit word.
Node* lead_offset = Int32Constant(0xD800 - (0x10000 >> 10));
// lead = (codepoint >> 10) + LEAD_OFFSET
Node* lead =
Int32Add(WordShr(codepoint, Int32Constant(10)), lead_offset);
// trail = (codepoint & 0x3FF) + 0xDC00;
Node* trail = Int32Add(Word32And(codepoint, Int32Constant(0x3FF)),
Int32Constant(0xDC00));
// codpoint = (trail << 16) | lead;
codepoint = Word32Or(WordShl(trail, Int32Constant(16)), lead);
break;
}
}
Node* value = AllocateSeqTwoByteString(2);
StoreNoWriteBarrier(
MachineRepresentation::kWord32, value,
IntPtrConstant(SeqTwoByteString::kHeaderSize - kHeapObjectTag),
codepoint);
var_result.Bind(value);
Goto(&return_result);
}
Bind(&return_result);
return var_result.value();
}
Node* CodeStubAssembler::StringToNumber(Node* context, Node* input) {
Label runtime(this, Label::kDeferred);
Label end(this);

View File

@ -19,6 +19,12 @@ 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
};
// Provides JavaScript-specific "macro-assembler" functionality on top of the
// CodeAssembler. By factoring the JavaScript-isms out of the CodeAssembler,
// it's possible to add JavaScript-specific useful CodeAssembler "macros"
@ -472,6 +478,9 @@ class CodeStubAssembler : public compiler::CodeAssembler {
compiler::Node* SubString(compiler::Node* context, compiler::Node* string,
compiler::Node* from, compiler::Node* to);
compiler::Node* StringFromCodePoint(compiler::Node* codepoint,
UnicodeEncoding encoding);
// Type conversion helpers.
// Convert a String to a Number.
compiler::Node* StringToNumber(compiler::Node* context,

View File

@ -785,7 +785,8 @@ void JSWeakMap::JSWeakMapVerify() {
void JSStringIterator::JSStringIteratorVerify() {
CHECK(IsJSStringIterator());
JSObjectVerify();
CHECK(string()->IsString());
CHECK(string()->IsSeqString() || string()->IsExternalString());
CHECK_GE(index(), 0);
CHECK_LE(index(), String::kMaxLength);
}

View File

@ -92,3 +92,11 @@ function TestNonOwnSlots() {
assertThrows(function() { object.next(); }, TypeError);
}
TestNonOwnSlots();
function TestSlicedStringRegression() {
var long_string = "abcdefhijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
var sliced_string = long_string.substring(1);
var iterator = sliced_string[Symbol.iterator]();
}
TestSlicedStringRegression();