Commit 460ba9c2 authored by jgruber's avatar jgruber Committed by Commit bot

[string] Refactor direct string conversions

This unifies several of the places in CSA that convert various
string kinds (cons, thin, sliced) to direct strings
(sequential, external).

A couple of spots remain with duplicate code, but most of these are
more difficult to unify due to specific optimizations.

BUG=

Review-Url: https://codereview.chromium.org/2744263002
Cr-Commit-Position: refs/heads/master@{#43848}
parent 9e827c23
...@@ -216,23 +216,23 @@ Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo( ...@@ -216,23 +216,23 @@ Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
} }
void RegExpBuiltinsAssembler::GetStringPointers( void RegExpBuiltinsAssembler::GetStringPointers(
Node* const string, Node* const offset, Node* const last_index, Node* const string_data, Node* const offset, Node* const last_index,
Node* const string_length, bool is_one_byte, Variable* var_string_start, Node* const string_length, String::Encoding encoding,
Variable* var_string_end) { Variable* var_string_start, Variable* var_string_end) {
DCHECK_EQ(var_string_start->rep(), MachineType::PointerRepresentation()); DCHECK_EQ(var_string_start->rep(), MachineType::PointerRepresentation());
DCHECK_EQ(var_string_end->rep(), MachineType::PointerRepresentation()); DCHECK_EQ(var_string_end->rep(), MachineType::PointerRepresentation());
STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize); const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
const int kHeaderSize = SeqOneByteString::kHeaderSize - kHeapObjectTag; ? UINT8_ELEMENTS
const ElementsKind kind = is_one_byte ? UINT8_ELEMENTS : UINT16_ELEMENTS; : UINT16_ELEMENTS;
Node* const from_offset = ElementOffsetFromIndex( Node* const from_offset = ElementOffsetFromIndex(
IntPtrAdd(offset, last_index), kind, INTPTR_PARAMETERS, kHeaderSize); IntPtrAdd(offset, last_index), kind, INTPTR_PARAMETERS);
var_string_start->Bind(IntPtrAdd(string, from_offset)); var_string_start->Bind(IntPtrAdd(string_data, from_offset));
Node* const to_offset = ElementOffsetFromIndex( Node* const to_offset = ElementOffsetFromIndex(
IntPtrAdd(offset, string_length), kind, INTPTR_PARAMETERS, kHeaderSize); IntPtrAdd(offset, string_length), kind, INTPTR_PARAMETERS);
var_string_end->Bind(IntPtrAdd(string, to_offset)); var_string_end->Bind(IntPtrAdd(string_data, to_offset));
} }
Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context, Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context,
...@@ -258,13 +258,9 @@ Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context, ...@@ -258,13 +258,9 @@ Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context,
Node* const int_zero = IntPtrConstant(0); Node* const int_zero = IntPtrConstant(0);
Variable var_result(this, MachineRepresentation::kTagged); ToDirectStringAssembler to_direct(state(), string);
Variable var_string(this, MachineType::PointerRepresentation(), int_zero);
Variable var_string_offset(this, MachineType::PointerRepresentation(),
int_zero);
Variable var_string_instance_type(this, MachineRepresentation::kWord32,
Int32Constant(0));
Variable var_result(this, MachineRepresentation::kTagged);
Label out(this), runtime(this, Label::kDeferred); Label out(this), runtime(this, Label::kDeferred);
// External constants. // External constants.
...@@ -308,19 +304,7 @@ Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context, ...@@ -308,19 +304,7 @@ Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context,
// Unpack the string if possible. // Unpack the string if possible.
var_string.Bind(BitcastTaggedToWord(string)); to_direct.TryToDirect(&runtime);
var_string_offset.Bind(int_zero);
var_string_instance_type.Bind(LoadInstanceType(string));
{
TryUnpackString(&var_string, &var_string_offset, &var_string_instance_type,
&runtime);
// At this point, {var_string} may contain a faked sequential string (i.e.
// an external string with an adjusted offset) so we cannot assert
// IsString({var_string}). We also cannot allocate after this point since
// GC could move {var_string}'s underlying string.
}
Node* const smi_string_length = LoadStringLength(string); Node* const smi_string_length = LoadStringLength(string);
...@@ -339,19 +323,16 @@ Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context, ...@@ -339,19 +323,16 @@ Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context,
{ {
Node* const int_string_length = SmiUntag(smi_string_length); Node* const int_string_length = SmiUntag(smi_string_length);
Node* const direct_string_data = to_direct.PointerToData(&runtime);
Node* const string_instance_type = var_string_instance_type.value();
CSA_ASSERT(this, IsSequentialStringInstanceType(string_instance_type));
Label next(this), if_isonebyte(this), if_istwobyte(this, Label::kDeferred); Label next(this), if_isonebyte(this), if_istwobyte(this, Label::kDeferred);
Branch(IsOneByteStringInstanceType(string_instance_type), &if_isonebyte, Branch(IsOneByteStringInstanceType(to_direct.instance_type()),
&if_istwobyte); &if_isonebyte, &if_istwobyte);
Bind(&if_isonebyte); Bind(&if_isonebyte);
{ {
const bool kIsOneByte = true; GetStringPointers(direct_string_data, to_direct.offset(), int_last_index,
GetStringPointers(var_string.value(), var_string_offset.value(), int_string_length, String::ONE_BYTE_ENCODING,
int_last_index, int_string_length, kIsOneByte,
&var_string_start, &var_string_end); &var_string_start, &var_string_end);
var_code.Bind( var_code.Bind(
LoadFixedArrayElement(data, JSRegExp::kIrregexpLatin1CodeIndex)); LoadFixedArrayElement(data, JSRegExp::kIrregexpLatin1CodeIndex));
...@@ -360,9 +341,8 @@ Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context, ...@@ -360,9 +341,8 @@ Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context,
Bind(&if_istwobyte); Bind(&if_istwobyte);
{ {
const bool kIsOneByte = false; GetStringPointers(direct_string_data, to_direct.offset(), int_last_index,
GetStringPointers(var_string.value(), var_string_offset.value(), int_string_length, String::TWO_BYTE_ENCODING,
int_last_index, int_string_length, kIsOneByte,
&var_string_start, &var_string_end); &var_string_start, &var_string_end);
var_code.Bind( var_code.Bind(
LoadFixedArrayElement(data, JSRegExp::kIrregexpUC16CodeIndex)); LoadFixedArrayElement(data, JSRegExp::kIrregexpUC16CodeIndex));
......
...@@ -34,10 +34,10 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler { ...@@ -34,10 +34,10 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
bool is_fastpath); bool is_fastpath);
// Loads {var_string_start} and {var_string_end} with the corresponding // Loads {var_string_start} and {var_string_end} with the corresponding
// offsets into the given {string}. // offsets into the given {string_data}.
void GetStringPointers(Node* const string, Node* const offset, void GetStringPointers(Node* const string_data, Node* const offset,
Node* const last_index, Node* const string_length, Node* const last_index, Node* const string_length,
bool is_one_byte, Variable* var_string_start, String::Encoding encoding, Variable* var_string_start,
Variable* var_string_end); Variable* var_string_end);
// Low level logic around the actual call into generated Irregexp code. // Low level logic around the actual call into generated Irregexp code.
......
...@@ -2904,6 +2904,22 @@ Node* CodeStubAssembler::IsSequentialStringInstanceType(Node* instance_type) { ...@@ -2904,6 +2904,22 @@ Node* CodeStubAssembler::IsSequentialStringInstanceType(Node* instance_type) {
Int32Constant(kSeqStringTag)); Int32Constant(kSeqStringTag));
} }
Node* CodeStubAssembler::IsExternalStringInstanceType(Node* instance_type) {
CSA_ASSERT(this, IsStringInstanceType(instance_type));
return Word32Equal(
Word32And(instance_type, Int32Constant(kStringRepresentationMask)),
Int32Constant(kExternalStringTag));
}
Node* CodeStubAssembler::IsShortExternalStringInstanceType(
Node* instance_type) {
CSA_ASSERT(this, IsStringInstanceType(instance_type));
STATIC_ASSERT(kShortExternalStringTag != 0);
return Word32NotEqual(
Word32And(instance_type, Int32Constant(kShortExternalStringMask)),
Int32Constant(0));
}
Node* CodeStubAssembler::IsJSReceiverInstanceType(Node* instance_type) { Node* CodeStubAssembler::IsJSReceiverInstanceType(Node* instance_type) {
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE); STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
return Int32GreaterThanOrEqual(instance_type, return Int32GreaterThanOrEqual(instance_type,
...@@ -3013,188 +3029,58 @@ Node* CodeStubAssembler::IsJSFunction(Node* object) { ...@@ -3013,188 +3029,58 @@ Node* CodeStubAssembler::IsJSFunction(Node* object) {
Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index, Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index,
ParameterMode parameter_mode) { ParameterMode parameter_mode) {
CSA_ASSERT(this, IsString(string)); CSA_ASSERT(this, IsString(string));
// Translate the {index} into a Word. // Translate the {index} into a Word.
index = ParameterToWord(index, parameter_mode); Node* const int_index = ParameterToWord(index, parameter_mode);
// We may need to loop in case of cons, thin, or sliced strings.
Variable var_index(this, MachineType::PointerRepresentation(), index);
Variable var_string(this, MachineRepresentation::kTagged, string);
Variable var_result(this, MachineRepresentation::kWord32); Variable var_result(this, MachineRepresentation::kWord32);
Variable* loop_vars[] = {&var_index, &var_string};
Label done_loop(this, &var_result), loop(this, 2, loop_vars);
Goto(&loop);
Bind(&loop);
{
// Load the current {index}.
index = var_index.value();
// Load the current {string}. Label out(this, &var_result), runtime_generic(this), runtime_external(this);
string = var_string.value();
// Load the instance type of the {string}. ToDirectStringAssembler to_direct(state(), string);
Node* string_instance_type = LoadInstanceType(string); Node* const direct_string = to_direct.TryToDirect(&runtime_generic);
Node* const offset = IntPtrAdd(int_index, to_direct.offset());
Node* const instance_type = to_direct.instance_type();
// Check if the {string} is a SeqString. Node* const string_data = to_direct.PointerToData(&runtime_external);
Label if_stringissequential(this), if_stringisnotsequential(this);
Branch(Word32Equal(Word32And(string_instance_type,
Int32Constant(kStringRepresentationMask)),
Int32Constant(kSeqStringTag)),
&if_stringissequential, &if_stringisnotsequential);
Bind(&if_stringissequential); // Check if the {string} is a TwoByteSeqString or a OneByteSeqString.
{ Label if_stringistwobyte(this), if_stringisonebyte(this);
// Check if the {string} is a TwoByteSeqString or a OneByteSeqString. Branch(IsOneByteStringInstanceType(instance_type), &if_stringisonebyte,
Label if_stringistwobyte(this), if_stringisonebyte(this); &if_stringistwobyte);
Branch(Word32Equal(Word32And(string_instance_type,
Int32Constant(kStringEncodingMask)),
Int32Constant(kTwoByteStringTag)),
&if_stringistwobyte, &if_stringisonebyte);
Bind(&if_stringisonebyte);
{
var_result.Bind(
Load(MachineType::Uint8(), string,
IntPtrAdd(index, IntPtrConstant(SeqOneByteString::kHeaderSize -
kHeapObjectTag))));
Goto(&done_loop);
}
Bind(&if_stringistwobyte);
{
var_result.Bind(
Load(MachineType::Uint16(), string,
IntPtrAdd(WordShl(index, IntPtrConstant(1)),
IntPtrConstant(SeqTwoByteString::kHeaderSize -
kHeapObjectTag))));
Goto(&done_loop);
}
}
Bind(&if_stringisnotsequential);
{
// Check if the {string} is a ConsString.
Label if_stringiscons(this), if_stringisnotcons(this);
Branch(Word32Equal(Word32And(string_instance_type,
Int32Constant(kStringRepresentationMask)),
Int32Constant(kConsStringTag)),
&if_stringiscons, &if_stringisnotcons);
Bind(&if_stringiscons);
{
// Check whether the right hand side is the empty string (i.e. if
// this is really a flat string in a cons string). If that is not
// the case we flatten the string first.
Label if_rhsisempty(this), if_rhsisnotempty(this, Label::kDeferred);
Node* rhs = LoadObjectField(string, ConsString::kSecondOffset);
Branch(WordEqual(rhs, EmptyStringConstant()), &if_rhsisempty,
&if_rhsisnotempty);
Bind(&if_rhsisempty);
{
// Just operate on the left hand side of the {string}.
var_string.Bind(LoadObjectField(string, ConsString::kFirstOffset));
Goto(&loop);
}
Bind(&if_rhsisnotempty);
{
// Flatten the {string} and lookup in the resulting string.
var_string.Bind(CallRuntime(Runtime::kFlattenString,
NoContextConstant(), string));
Goto(&loop);
}
}
Bind(&if_stringisnotcons); Bind(&if_stringisonebyte);
{ {
// Check if the {string} is an ExternalString. var_result.Bind(Load(MachineType::Uint8(), string_data, offset));
Label if_stringisexternal(this), if_stringisnotexternal(this); Goto(&out);
Branch(Word32Equal(Word32And(string_instance_type, }
Int32Constant(kStringRepresentationMask)),
Int32Constant(kExternalStringTag)),
&if_stringisexternal, &if_stringisnotexternal);
Bind(&if_stringisexternal);
{
// Check if the {string} is a short external string.
Label if_stringisnotshort(this),
if_stringisshort(this, Label::kDeferred);
Branch(Word32Equal(Word32And(string_instance_type,
Int32Constant(kShortExternalStringMask)),
Int32Constant(0)),
&if_stringisnotshort, &if_stringisshort);
Bind(&if_stringisnotshort);
{
// Load the actual resource data from the {string}.
Node* string_resource_data =
LoadObjectField(string, ExternalString::kResourceDataOffset,
MachineType::Pointer());
// Check if the {string} is a TwoByteExternalString or a
// OneByteExternalString.
Label if_stringistwobyte(this), if_stringisonebyte(this);
Branch(Word32Equal(Word32And(string_instance_type,
Int32Constant(kStringEncodingMask)),
Int32Constant(kTwoByteStringTag)),
&if_stringistwobyte, &if_stringisonebyte);
Bind(&if_stringisonebyte);
{
var_result.Bind(
Load(MachineType::Uint8(), string_resource_data, index));
Goto(&done_loop);
}
Bind(&if_stringistwobyte); Bind(&if_stringistwobyte);
{ {
var_result.Bind(Load(MachineType::Uint16(), string_resource_data, var_result.Bind(Load(MachineType::Uint16(), string_data,
WordShl(index, IntPtrConstant(1)))); WordShl(offset, IntPtrConstant(1))));
Goto(&done_loop); Goto(&out);
} }
}
Bind(&if_stringisshort); Bind(&runtime_generic);
{ {
// The {string} might be compressed, call the runtime. Node* const smi_index = ParameterToTagged(index, parameter_mode);
var_result.Bind(SmiToWord32( Node* const result = CallRuntime(Runtime::kStringCharCodeAtRT,
CallRuntime(Runtime::kExternalStringGetChar, NoContextConstant(), string, smi_index);
NoContextConstant(), string, SmiTag(index)))); var_result.Bind(SmiToWord32(result));
Goto(&done_loop); Goto(&out);
} }
}
Bind(&if_stringisnotexternal); Bind(&runtime_external);
{ {
Label if_stringissliced(this), if_stringisthin(this); Node* const result =
Branch( CallRuntime(Runtime::kExternalStringGetChar, NoContextConstant(),
Word32Equal(Word32And(string_instance_type, direct_string, SmiTag(offset));
Int32Constant(kStringRepresentationMask)), var_result.Bind(SmiToWord32(result));
Int32Constant(kSlicedStringTag)), Goto(&out);
&if_stringissliced, &if_stringisthin);
Bind(&if_stringissliced);
{
// The {string} is a SlicedString, continue with its parent.
Node* string_offset =
LoadAndUntagObjectField(string, SlicedString::kOffsetOffset);
Node* string_parent =
LoadObjectField(string, SlicedString::kParentOffset);
var_index.Bind(IntPtrAdd(index, string_offset));
var_string.Bind(string_parent);
Goto(&loop);
}
Bind(&if_stringisthin);
{
// The {string} is a ThinString, continue with its actual value.
var_string.Bind(LoadObjectField(string, ThinString::kActualOffset));
Goto(&loop);
}
}
}
}
} }
Bind(&done_loop); Bind(&out);
return var_result.value(); return var_result.value();
} }
...@@ -3313,27 +3199,14 @@ Node* AllocAndCopyStringCharacters(CodeStubAssembler* a, Node* context, ...@@ -3313,27 +3199,14 @@ Node* AllocAndCopyStringCharacters(CodeStubAssembler* a, Node* context,
Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
Node* to) { Node* to) {
Label end(this); Variable var_result(this, MachineRepresentation::kTagged);
Label runtime(this); ToDirectStringAssembler to_direct(state(), string);
Label end(this), runtime(this);
Node* const int_zero = Int32Constant(0);
// Int32 variables.
Variable var_instance_type(this, MachineRepresentation::kWord32, int_zero);
Variable var_representation(this, MachineRepresentation::kWord32, int_zero);
Variable var_from(this, MachineRepresentation::kTagged, from); // Smi.
Variable var_string(this, MachineRepresentation::kTagged, string); // String.
Variable var_result(this, MachineRepresentation::kTagged); // String.
// Make sure first argument is a string. // Make sure first argument is a string.
CSA_ASSERT(this, TaggedIsNotSmi(string)); CSA_ASSERT(this, TaggedIsNotSmi(string));
CSA_ASSERT(this, IsString(string)); CSA_ASSERT(this, IsString(string));
// Load the instance type of the {string}.
Node* const instance_type = LoadInstanceType(string);
var_instance_type.Bind(instance_type);
// Make sure that both from and to are non-negative smis. // Make sure that both from and to are non-negative smis.
GotoIfNot(TaggedIsPositiveSmi(from), &runtime); GotoIfNot(TaggedIsPositiveSmi(from), &runtime);
...@@ -3356,132 +3229,56 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, ...@@ -3356,132 +3229,56 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
// TODO(jgruber): Add an additional case for substring of length == 0? // TODO(jgruber): Add an additional case for substring of length == 0?
// Deal with different string types: update the index if necessary // Deal with different string types: update the index if necessary
// and put the underlying string into var_string. // and extract the underlying string.
// If the string is not indirect, it can only be sequential or external.
STATIC_ASSERT(kIsIndirectStringMask ==
(kSlicedStringTag & kConsStringTag & kThinStringTag));
STATIC_ASSERT(kIsIndirectStringMask != 0);
Label underlying_unpacked(this);
GotoIf(Word32Equal(
Word32And(instance_type, Int32Constant(kIsIndirectStringMask)),
Int32Constant(0)),
&underlying_unpacked);
// The subject string is a sliced, cons, or thin string.
Label thin_string(this), thin_or_sliced(this);
var_representation.Bind(
Word32And(instance_type, Int32Constant(kStringRepresentationMask)));
GotoIf(
Word32NotEqual(var_representation.value(), Int32Constant(kConsStringTag)),
&thin_or_sliced);
// Cons string. Check whether it is flat, then fetch first part. Node* const direct_string = to_direct.TryToDirect(&runtime);
// Flat cons strings have an empty second part. Node* const offset = SmiAdd(from, SmiTag(to_direct.offset()));
{ Node* const instance_type = to_direct.instance_type();
GotoIf(WordNotEqual(LoadObjectField(string, ConsString::kSecondOffset),
EmptyStringConstant()),
&runtime);
Node* first_string_part = LoadObjectField(string, ConsString::kFirstOffset);
var_string.Bind(first_string_part);
var_instance_type.Bind(LoadInstanceType(first_string_part));
var_representation.Bind(Word32And(
var_instance_type.value(), Int32Constant(kStringRepresentationMask)));
// The loaded first part might be a thin string.
Branch(Word32Equal(Word32And(var_instance_type.value(),
Int32Constant(kIsIndirectStringMask)),
Int32Constant(0)),
&underlying_unpacked, &thin_string);
}
Bind(&thin_or_sliced);
{
GotoIf(
Word32Equal(var_representation.value(), Int32Constant(kThinStringTag)),
&thin_string);
// Otherwise it's a sliced string.
// Fetch parent and correct start index by offset.
Node* sliced_offset =
LoadObjectField(var_string.value(), SlicedString::kOffsetOffset);
var_from.Bind(SmiAdd(from, sliced_offset));
Node* slice_parent = LoadObjectField(string, SlicedString::kParentOffset);
var_string.Bind(slice_parent);
Node* slice_parent_instance_type = LoadInstanceType(slice_parent);
var_instance_type.Bind(slice_parent_instance_type);
// The loaded parent might be a thin string.
Branch(Word32Equal(Word32And(var_instance_type.value(),
Int32Constant(kIsIndirectStringMask)),
Int32Constant(0)),
&underlying_unpacked, &thin_string);
}
Bind(&thin_string);
{
Node* actual_string =
LoadObjectField(var_string.value(), ThinString::kActualOffset);
var_string.Bind(actual_string);
var_instance_type.Bind(LoadInstanceType(actual_string));
Goto(&underlying_unpacked);
}
// The subject string can only be external or sequential string of either // The subject string can only be external or sequential string of either
// encoding at this point. // encoding at this point.
Label external_string(this); Label external_string(this);
Bind(&underlying_unpacked);
{ {
if (FLAG_string_slices) { if (FLAG_string_slices) {
Label copy_routine(this); Label next(this);
// Short slice. Copy instead of slicing. // Short slice. Copy instead of slicing.
GotoIf(SmiLessThan(substr_length, GotoIf(SmiLessThan(substr_length,
SmiConstant(Smi::FromInt(SlicedString::kMinLength))), SmiConstant(Smi::FromInt(SlicedString::kMinLength))),
&copy_routine); &next);
// Allocate new sliced string. // Allocate new sliced string.
Label two_byte_slice(this);
STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
Counters* counters = isolate()->counters(); Counters* counters = isolate()->counters();
IncrementCounter(counters->sub_string_native(), 1); IncrementCounter(counters->sub_string_native(), 1);
GotoIf(Word32Equal(Word32And(var_instance_type.value(), Label one_byte_slice(this), two_byte_slice(this);
Int32Constant(kStringEncodingMask)), Branch(IsOneByteStringInstanceType(to_direct.instance_type()),
Int32Constant(0)), &one_byte_slice, &two_byte_slice);
&two_byte_slice);
var_result.Bind(AllocateSlicedOneByteString( Bind(&one_byte_slice);
substr_length, var_string.value(), var_from.value())); {
Goto(&end); var_result.Bind(
AllocateSlicedOneByteString(substr_length, direct_string, offset));
Goto(&end);
}
Bind(&two_byte_slice); Bind(&two_byte_slice);
{
var_result.Bind(
AllocateSlicedTwoByteString(substr_length, direct_string, offset));
Goto(&end);
}
var_result.Bind(AllocateSlicedTwoByteString( Bind(&next);
substr_length, var_string.value(), var_from.value()));
Goto(&end);
Bind(&copy_routine);
} }
// The subject string can only be external or sequential string of either // The subject string can only be external or sequential string of either
// encoding at this point. // encoding at this point.
STATIC_ASSERT(kExternalStringTag != 0); GotoIf(to_direct.is_external(), &external_string);
STATIC_ASSERT(kSeqStringTag == 0);
GotoIfNot(Word32Equal(Word32And(var_instance_type.value(),
Int32Constant(kExternalStringTag)),
Int32Constant(0)),
&external_string);
var_result.Bind(AllocAndCopyStringCharacters( var_result.Bind(AllocAndCopyStringCharacters(
this, context, var_string.value(), var_instance_type.value(), this, context, direct_string, instance_type, offset, substr_length));
var_from.value(), substr_length));
Counters* counters = isolate()->counters(); Counters* counters = isolate()->counters();
IncrementCounter(counters->sub_string_native(), 1); IncrementCounter(counters->sub_string_native(), 1);
...@@ -3492,12 +3289,11 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, ...@@ -3492,12 +3289,11 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
// Handle external string. // Handle external string.
Bind(&external_string); Bind(&external_string);
{ {
Node* const fake_sequential_string = TryDerefExternalString( Node* const fake_sequential_string = to_direct.PointerToString(&runtime);
var_string.value(), var_instance_type.value(), &runtime);
var_result.Bind(AllocAndCopyStringCharacters( var_result.Bind(
this, context, fake_sequential_string, var_instance_type.value(), AllocAndCopyStringCharacters(this, context, fake_sequential_string,
var_from.value(), substr_length)); instance_type, offset, substr_length));
Counters* counters = isolate()->counters(); Counters* counters = isolate()->counters();
IncrementCounter(counters->sub_string_native(), 1); IncrementCounter(counters->sub_string_native(), 1);
...@@ -3508,7 +3304,7 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, ...@@ -3508,7 +3304,7 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
// Substrings of length 1 are generated through CharCodeAt and FromCharCode. // Substrings of length 1 are generated through CharCodeAt and FromCharCode.
Bind(&single_char); Bind(&single_char);
{ {
Node* char_code = StringCharCodeAt(var_string.value(), var_from.value()); Node* char_code = StringCharCodeAt(string, from);
var_result.Bind(StringFromCharCode(char_code)); var_result.Bind(StringFromCharCode(char_code));
Goto(&end); Goto(&end);
} }
...@@ -3542,45 +3338,30 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from, ...@@ -3542,45 +3338,30 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
return var_result.value(); return var_result.value();
} }
namespace { ToDirectStringAssembler::ToDirectStringAssembler(
compiler::CodeAssemblerState* state, Node* string)
Node* IsExternalStringInstanceType(CodeStubAssembler* a, : CodeStubAssembler(state),
Node* const instance_type) { var_string_(this, MachineRepresentation::kTagged, string),
CSA_ASSERT(a, a->IsStringInstanceType(instance_type)); var_instance_type_(this, MachineRepresentation::kWord32),
return a->Word32Equal( var_offset_(this, MachineType::PointerRepresentation()),
a->Word32And(instance_type, a->Int32Constant(kStringRepresentationMask)), var_is_external_(this, MachineRepresentation::kWord32) {
a->Int32Constant(kExternalStringTag)); CSA_ASSERT(this, TaggedIsNotSmi(string));
} CSA_ASSERT(this, IsString(string));
Node* IsShortExternalStringInstanceType(CodeStubAssembler* a, var_string_.Bind(string);
Node* const instance_type) { var_offset_.Bind(IntPtrConstant(0));
CSA_ASSERT(a, a->IsStringInstanceType(instance_type)); var_instance_type_.Bind(LoadInstanceType(string));
STATIC_ASSERT(kShortExternalStringTag != 0); var_is_external_.Bind(Int32Constant(0));
return a->Word32NotEqual(
a->Word32And(instance_type, a->Int32Constant(kShortExternalStringMask)),
a->Int32Constant(0));
} }
} // namespace Node* ToDirectStringAssembler::TryToDirect(Label* if_bailout) {
VariableList vars({&var_string_, &var_offset_, &var_instance_type_}, zone());
void CodeStubAssembler::TryUnpackString(Variable* var_string,
Variable* var_offset,
Variable* var_instance_type,
Label* if_bailout) {
DCHECK_EQ(var_string->rep(), MachineType::PointerRepresentation());
DCHECK_EQ(var_offset->rep(), MachineType::PointerRepresentation());
DCHECK_EQ(var_instance_type->rep(), MachineRepresentation::kWord32);
CSA_ASSERT(this, IsString(var_string->value()));
Label out(this);
VariableList vars({var_string, var_offset, var_instance_type}, zone());
Label dispatch(this, vars); Label dispatch(this, vars);
Label if_isdirect(this); Label if_iscons(this);
Label if_iscons(this, Label::kDeferred); Label if_isexternal(this);
Label if_isexternal(this, Label::kDeferred); Label if_issliced(this);
Label if_issliced(this, Label::kDeferred); Label if_isthin(this);
Label if_isthin(this, Label::kDeferred); Label out(this);
Goto(&dispatch); Goto(&dispatch);
...@@ -3592,12 +3373,12 @@ void CodeStubAssembler::TryUnpackString(Variable* var_string, ...@@ -3592,12 +3373,12 @@ void CodeStubAssembler::TryUnpackString(Variable* var_string,
kSlicedStringTag, kThinStringTag, kSlicedStringTag, kThinStringTag,
}; };
Label* labels[] = { Label* labels[] = {
&if_isdirect, &if_iscons, &if_isexternal, &if_issliced, &if_isthin, &out, &if_iscons, &if_isexternal, &if_issliced, &if_isthin,
}; };
STATIC_ASSERT(arraysize(values) == arraysize(labels)); STATIC_ASSERT(arraysize(values) == arraysize(labels));
Node* const representation = Word32And( Node* const representation = Word32And(
var_instance_type->value(), Int32Constant(kStringRepresentationMask)); var_instance_type_.value(), Int32Constant(kStringRepresentationMask));
Switch(representation, if_bailout, values, labels, arraysize(values)); Switch(representation, if_bailout, values, labels, arraysize(values));
} }
...@@ -3605,13 +3386,13 @@ void CodeStubAssembler::TryUnpackString(Variable* var_string, ...@@ -3605,13 +3386,13 @@ void CodeStubAssembler::TryUnpackString(Variable* var_string,
// Flat cons strings have an empty second part. // Flat cons strings have an empty second part.
Bind(&if_iscons); Bind(&if_iscons);
{ {
Node* const string = var_string->value(); Node* const string = var_string_.value();
GotoIfNot(IsEmptyString(LoadObjectField(string, ConsString::kSecondOffset)), GotoIfNot(IsEmptyString(LoadObjectField(string, ConsString::kSecondOffset)),
if_bailout); if_bailout);
Node* const lhs = LoadObjectField(string, ConsString::kFirstOffset); Node* const lhs = LoadObjectField(string, ConsString::kFirstOffset);
var_string->Bind(BitcastTaggedToWord(lhs)); var_string_.Bind(lhs);
var_instance_type->Bind(LoadInstanceType(lhs)); var_instance_type_.Bind(LoadInstanceType(lhs));
Goto(&dispatch); Goto(&dispatch);
} }
...@@ -3619,14 +3400,14 @@ void CodeStubAssembler::TryUnpackString(Variable* var_string, ...@@ -3619,14 +3400,14 @@ void CodeStubAssembler::TryUnpackString(Variable* var_string,
// Sliced string. Fetch parent and correct start index by offset. // Sliced string. Fetch parent and correct start index by offset.
Bind(&if_issliced); Bind(&if_issliced);
{ {
Node* const string = var_string->value(); Node* const string = var_string_.value();
Node* const sliced_offset = Node* const sliced_offset =
LoadObjectField(string, SlicedString::kOffsetOffset); LoadAndUntagObjectField(string, SlicedString::kOffsetOffset);
var_offset->Bind(IntPtrAdd(var_offset->value(), SmiUntag(sliced_offset))); var_offset_.Bind(IntPtrAdd(var_offset_.value(), sliced_offset));
Node* const parent = LoadObjectField(string, SlicedString::kParentOffset); Node* const parent = LoadObjectField(string, SlicedString::kParentOffset);
var_string->Bind(BitcastTaggedToWord(parent)); var_string_.Bind(parent);
var_instance_type->Bind(LoadInstanceType(parent)); var_instance_type_.Bind(LoadInstanceType(parent));
Goto(&dispatch); Goto(&dispatch);
} }
...@@ -3634,36 +3415,65 @@ void CodeStubAssembler::TryUnpackString(Variable* var_string, ...@@ -3634,36 +3415,65 @@ void CodeStubAssembler::TryUnpackString(Variable* var_string,
// Thin string. Fetch the actual string. // Thin string. Fetch the actual string.
Bind(&if_isthin); Bind(&if_isthin);
{ {
Node* const string = var_string->value(); Node* const string = var_string_.value();
Node* const actual_string = Node* const actual_string =
LoadObjectField(string, ThinString::kActualOffset); LoadObjectField(string, ThinString::kActualOffset);
Node* const actual_instance_type = LoadInstanceType(actual_string); Node* const actual_instance_type = LoadInstanceType(actual_string);
var_string->Bind(BitcastTaggedToWord(actual_string)); var_string_.Bind(actual_string);
var_instance_type->Bind(actual_instance_type); var_instance_type_.Bind(actual_instance_type);
Goto(&dispatch); Goto(&dispatch);
} }
// External string. // External string.
Bind(&if_isexternal); Bind(&if_isexternal);
{ var_is_external_.Bind(Int32Constant(1));
Node* const string = var_string->value(); Goto(&out);
Node* const faked_seq_string =
TryDerefExternalString(string, var_instance_type->value(), if_bailout);
STATIC_ASSERT(kSeqStringTag == 0x0); Bind(&out);
Node* const faked_seq_instance_type = Word32Xor( return var_string_.value();
var_instance_type->value(), Int32Constant(kExternalStringTag)); }
CSA_ASSERT(this, IsSequentialStringInstanceType(faked_seq_instance_type));
Node* ToDirectStringAssembler::TryToSequential(StringPointerKind ptr_kind,
Label* if_bailout) {
CHECK(ptr_kind == PTR_TO_DATA || ptr_kind == PTR_TO_STRING);
Variable var_result(this, MachineType::PointerRepresentation());
Label out(this), if_issequential(this), if_isexternal(this);
Branch(is_external(), &if_isexternal, &if_issequential);
Bind(&if_issequential);
{
STATIC_ASSERT(SeqOneByteString::kHeaderSize ==
SeqTwoByteString::kHeaderSize);
Node* result = BitcastTaggedToWord(var_string_.value());
if (ptr_kind == PTR_TO_DATA) {
result = IntPtrAdd(result, IntPtrConstant(SeqOneByteString::kHeaderSize -
kHeapObjectTag));
}
var_result.Bind(result);
Goto(&out);
}
var_string->Bind(faked_seq_string); Bind(&if_isexternal);
var_instance_type->Bind(faked_seq_instance_type); {
GotoIf(IsShortExternalStringInstanceType(var_instance_type_.value()),
if_bailout);
Goto(&if_isdirect); Node* const string = var_string_.value();
Node* result = LoadObjectField(string, ExternalString::kResourceDataOffset,
MachineType::Pointer());
if (ptr_kind == PTR_TO_STRING) {
result = IntPtrSub(result, IntPtrConstant(SeqOneByteString::kHeaderSize -
kHeapObjectTag));
}
var_result.Bind(result);
Goto(&out);
} }
Bind(&if_isdirect); Bind(&out);
return var_result.value();
} }
Node* CodeStubAssembler::TryDerefExternalString(Node* const string, Node* CodeStubAssembler::TryDerefExternalString(Node* const string,
...@@ -3671,9 +3481,8 @@ Node* CodeStubAssembler::TryDerefExternalString(Node* const string, ...@@ -3671,9 +3481,8 @@ Node* CodeStubAssembler::TryDerefExternalString(Node* const string,
Label* if_bailout) { Label* if_bailout) {
Label out(this); Label out(this);
USE(IsExternalStringInstanceType); CSA_ASSERT(this, IsExternalStringInstanceType(instance_type));
CSA_ASSERT(this, IsExternalStringInstanceType(this, instance_type)); GotoIf(IsShortExternalStringInstanceType(instance_type), if_bailout);
GotoIf(IsShortExternalStringInstanceType(this, instance_type), if_bailout);
// Move the pointer so that offset-wise, it looks like a sequential string. // Move the pointer so that offset-wise, it looks like a sequential string.
STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize); STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
......
...@@ -692,6 +692,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { ...@@ -692,6 +692,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* IsSpecialReceiverInstanceType(Node* instance_type); Node* IsSpecialReceiverInstanceType(Node* instance_type);
Node* IsStringInstanceType(Node* instance_type); Node* IsStringInstanceType(Node* instance_type);
Node* IsOneByteStringInstanceType(Node* instance_type); Node* IsOneByteStringInstanceType(Node* instance_type);
Node* IsExternalStringInstanceType(Node* instance_type);
Node* IsShortExternalStringInstanceType(Node* instance_type);
Node* IsSequentialStringInstanceType(Node* instance_type); Node* IsSequentialStringInstanceType(Node* instance_type);
Node* IsString(Node* object); Node* IsString(Node* object);
Node* IsJSObject(Node* object); Node* IsJSObject(Node* object);
...@@ -738,14 +740,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { ...@@ -738,14 +740,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* StringAdd(Node* context, Node* first, Node* second, Node* StringAdd(Node* context, Node* first, Node* second,
AllocationFlags flags = kNone); AllocationFlags flags = kNone);
// Tries to unpack |string| into a pseudo-sequential string. For instance,
// In addition to the work done by TryDerefExternalString and
// MaybeDerefIndirectString, this method can also unpack sliced strings into
// a (string, offset) pair. The same GC restrictions on the returned string
// value apply as for TryDerefExternalString.
void TryUnpackString(Variable* var_string, Variable* var_offset,
Variable* var_instance_type, Label* if_bailout);
// Unpack the external string, returning a pointer that (offset-wise) looks // Unpack the external string, returning a pointer that (offset-wise) looks
// like a sequential string. // like a sequential string.
// Note that this pointer is not tagged and does not point to a real // Note that this pointer is not tagged and does not point to a real
...@@ -1399,6 +1393,42 @@ class CodeStubArguments { ...@@ -1399,6 +1393,42 @@ class CodeStubArguments {
Node* fp_; Node* fp_;
}; };
class ToDirectStringAssembler : public CodeStubAssembler {
private:
enum StringPointerKind { PTR_TO_DATA, PTR_TO_STRING };
public:
explicit ToDirectStringAssembler(compiler::CodeAssemblerState* state,
Node* string);
// Converts flat cons, thin, and sliced strings and returns the direct
// string. The result can be either a sequential or external string.
Node* TryToDirect(Label* if_bailout);
// Returns a pointer to the beginning of the string data.
Node* PointerToData(Label* if_bailout) {
return TryToSequential(PTR_TO_DATA, if_bailout);
}
// Returns a pointer that, offset-wise, looks like a String.
Node* PointerToString(Label* if_bailout) {
return TryToSequential(PTR_TO_STRING, if_bailout);
}
Node* string() { return var_string_.value(); }
Node* instance_type() { return var_instance_type_.value(); }
Node* offset() { return var_offset_.value(); }
Node* is_external() { return var_is_external_.value(); }
private:
Node* TryToSequential(StringPointerKind ptr_kind, Label* if_bailout);
Variable var_string_;
Variable var_instance_type_;
Variable var_offset_;
Variable var_is_external_;
};
#ifdef DEBUG #ifdef DEBUG
#define CSA_ASSERT(csa, x) \ #define CSA_ASSERT(csa, x) \
(csa)->Assert([&] { return (x); }, #x, __FILE__, __LINE__) (csa)->Assert([&] { return (x); }, #x, __FILE__, __LINE__)
......
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