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(
}
void RegExpBuiltinsAssembler::GetStringPointers(
Node* const string, Node* const offset, Node* const last_index,
Node* const string_length, bool is_one_byte, Variable* var_string_start,
Variable* var_string_end) {
Node* const string_data, Node* const offset, Node* const last_index,
Node* const string_length, String::Encoding encoding,
Variable* var_string_start, Variable* var_string_end) {
DCHECK_EQ(var_string_start->rep(), MachineType::PointerRepresentation());
DCHECK_EQ(var_string_end->rep(), MachineType::PointerRepresentation());
STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
const int kHeaderSize = SeqOneByteString::kHeaderSize - kHeapObjectTag;
const ElementsKind kind = is_one_byte ? UINT8_ELEMENTS : UINT16_ELEMENTS;
const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
? UINT8_ELEMENTS
: UINT16_ELEMENTS;
Node* const from_offset = ElementOffsetFromIndex(
IntPtrAdd(offset, last_index), kind, INTPTR_PARAMETERS, kHeaderSize);
var_string_start->Bind(IntPtrAdd(string, from_offset));
IntPtrAdd(offset, last_index), kind, INTPTR_PARAMETERS);
var_string_start->Bind(IntPtrAdd(string_data, from_offset));
Node* const to_offset = ElementOffsetFromIndex(
IntPtrAdd(offset, string_length), kind, INTPTR_PARAMETERS, kHeaderSize);
var_string_end->Bind(IntPtrAdd(string, to_offset));
IntPtrAdd(offset, string_length), kind, INTPTR_PARAMETERS);
var_string_end->Bind(IntPtrAdd(string_data, to_offset));
}
Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context,
......@@ -258,13 +258,9 @@ Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context,
Node* const int_zero = IntPtrConstant(0);
Variable var_result(this, MachineRepresentation::kTagged);
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));
ToDirectStringAssembler to_direct(state(), string);
Variable var_result(this, MachineRepresentation::kTagged);
Label out(this), runtime(this, Label::kDeferred);
// External constants.
......@@ -308,19 +304,7 @@ Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context,
// Unpack the string if possible.
var_string.Bind(BitcastTaggedToWord(string));
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.
}
to_direct.TryToDirect(&runtime);
Node* const smi_string_length = LoadStringLength(string);
......@@ -339,19 +323,16 @@ Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context,
{
Node* const int_string_length = SmiUntag(smi_string_length);
Node* const string_instance_type = var_string_instance_type.value();
CSA_ASSERT(this, IsSequentialStringInstanceType(string_instance_type));
Node* const direct_string_data = to_direct.PointerToData(&runtime);
Label next(this), if_isonebyte(this), if_istwobyte(this, Label::kDeferred);
Branch(IsOneByteStringInstanceType(string_instance_type), &if_isonebyte,
&if_istwobyte);
Branch(IsOneByteStringInstanceType(to_direct.instance_type()),
&if_isonebyte, &if_istwobyte);
Bind(&if_isonebyte);
{
const bool kIsOneByte = true;
GetStringPointers(var_string.value(), var_string_offset.value(),
int_last_index, int_string_length, kIsOneByte,
GetStringPointers(direct_string_data, to_direct.offset(), int_last_index,
int_string_length, String::ONE_BYTE_ENCODING,
&var_string_start, &var_string_end);
var_code.Bind(
LoadFixedArrayElement(data, JSRegExp::kIrregexpLatin1CodeIndex));
......@@ -360,9 +341,8 @@ Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context,
Bind(&if_istwobyte);
{
const bool kIsOneByte = false;
GetStringPointers(var_string.value(), var_string_offset.value(),
int_last_index, int_string_length, kIsOneByte,
GetStringPointers(direct_string_data, to_direct.offset(), int_last_index,
int_string_length, String::TWO_BYTE_ENCODING,
&var_string_start, &var_string_end);
var_code.Bind(
LoadFixedArrayElement(data, JSRegExp::kIrregexpUC16CodeIndex));
......
......@@ -34,10 +34,10 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
bool is_fastpath);
// Loads {var_string_start} and {var_string_end} with the corresponding
// offsets into the given {string}.
void GetStringPointers(Node* const string, Node* const offset,
// offsets into the given {string_data}.
void GetStringPointers(Node* const string_data, Node* const offset,
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);
// Low level logic around the actual call into generated Irregexp code.
......
......@@ -2904,6 +2904,22 @@ Node* CodeStubAssembler::IsSequentialStringInstanceType(Node* instance_type) {
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) {
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
return Int32GreaterThanOrEqual(instance_type,
......@@ -3013,188 +3029,58 @@ Node* CodeStubAssembler::IsJSFunction(Node* object) {
Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index,
ParameterMode parameter_mode) {
CSA_ASSERT(this, IsString(string));
// 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* 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}.
string = var_string.value();
Label out(this, &var_result), runtime_generic(this), runtime_external(this);
// Load the instance type of the {string}.
Node* string_instance_type = LoadInstanceType(string);
ToDirectStringAssembler to_direct(state(), 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.
Label if_stringissequential(this), if_stringisnotsequential(this);
Branch(Word32Equal(Word32And(string_instance_type,
Int32Constant(kStringRepresentationMask)),
Int32Constant(kSeqStringTag)),
&if_stringissequential, &if_stringisnotsequential);
Node* const string_data = to_direct.PointerToData(&runtime_external);
Bind(&if_stringissequential);
{
// Check if the {string} is a TwoByteSeqString or a OneByteSeqString.
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,
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);
}
}
// Check if the {string} is a TwoByteSeqString or a OneByteSeqString.
Label if_stringistwobyte(this), if_stringisonebyte(this);
Branch(IsOneByteStringInstanceType(instance_type), &if_stringisonebyte,
&if_stringistwobyte);
Bind(&if_stringisnotcons);
{
// Check if the {string} is an ExternalString.
Label if_stringisexternal(this), if_stringisnotexternal(this);
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_stringisonebyte);
{
var_result.Bind(Load(MachineType::Uint8(), string_data, offset));
Goto(&out);
}
Bind(&if_stringistwobyte);
{
var_result.Bind(Load(MachineType::Uint16(), string_resource_data,
WordShl(index, IntPtrConstant(1))));
Goto(&done_loop);
}
}
Bind(&if_stringistwobyte);
{
var_result.Bind(Load(MachineType::Uint16(), string_data,
WordShl(offset, IntPtrConstant(1))));
Goto(&out);
}
Bind(&if_stringisshort);
{
// The {string} might be compressed, call the runtime.
var_result.Bind(SmiToWord32(
CallRuntime(Runtime::kExternalStringGetChar,
NoContextConstant(), string, SmiTag(index))));
Goto(&done_loop);
}
}
Bind(&runtime_generic);
{
Node* const smi_index = ParameterToTagged(index, parameter_mode);
Node* const result = CallRuntime(Runtime::kStringCharCodeAtRT,
NoContextConstant(), string, smi_index);
var_result.Bind(SmiToWord32(result));
Goto(&out);
}
Bind(&if_stringisnotexternal);
{
Label if_stringissliced(this), if_stringisthin(this);
Branch(
Word32Equal(Word32And(string_instance_type,
Int32Constant(kStringRepresentationMask)),
Int32Constant(kSlicedStringTag)),
&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(&runtime_external);
{
Node* const result =
CallRuntime(Runtime::kExternalStringGetChar, NoContextConstant(),
direct_string, SmiTag(offset));
var_result.Bind(SmiToWord32(result));
Goto(&out);
}
Bind(&done_loop);
Bind(&out);
return var_result.value();
}
......@@ -3313,27 +3199,14 @@ Node* AllocAndCopyStringCharacters(CodeStubAssembler* a, Node* context,
Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
Node* to) {
Label end(this);
Label 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.
Variable var_result(this, MachineRepresentation::kTagged);
ToDirectStringAssembler to_direct(state(), string);
Label end(this), runtime(this);
// Make sure first argument is a string.
CSA_ASSERT(this, TaggedIsNotSmi(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.
GotoIfNot(TaggedIsPositiveSmi(from), &runtime);
......@@ -3356,132 +3229,56 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
// TODO(jgruber): Add an additional case for substring of length == 0?
// Deal with different string types: update the index if necessary
// and put the underlying string into var_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);
// and extract the underlying string.
// Cons string. Check whether it is flat, then fetch first part.
// Flat cons strings have an empty second part.
{
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);
}
Node* const direct_string = to_direct.TryToDirect(&runtime);
Node* const offset = SmiAdd(from, SmiTag(to_direct.offset()));
Node* const instance_type = to_direct.instance_type();
// The subject string can only be external or sequential string of either
// encoding at this point.
Label external_string(this);
Bind(&underlying_unpacked);
{
if (FLAG_string_slices) {
Label copy_routine(this);
Label next(this);
// Short slice. Copy instead of slicing.
GotoIf(SmiLessThan(substr_length,
SmiConstant(Smi::FromInt(SlicedString::kMinLength))),
&copy_routine);
&next);
// Allocate new sliced string.
Label two_byte_slice(this);
STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
Counters* counters = isolate()->counters();
IncrementCounter(counters->sub_string_native(), 1);
GotoIf(Word32Equal(Word32And(var_instance_type.value(),
Int32Constant(kStringEncodingMask)),
Int32Constant(0)),
&two_byte_slice);
Label one_byte_slice(this), two_byte_slice(this);
Branch(IsOneByteStringInstanceType(to_direct.instance_type()),
&one_byte_slice, &two_byte_slice);
var_result.Bind(AllocateSlicedOneByteString(
substr_length, var_string.value(), var_from.value()));
Goto(&end);
Bind(&one_byte_slice);
{
var_result.Bind(
AllocateSlicedOneByteString(substr_length, direct_string, offset));
Goto(&end);
}
Bind(&two_byte_slice);
{
var_result.Bind(
AllocateSlicedTwoByteString(substr_length, direct_string, offset));
Goto(&end);
}
var_result.Bind(AllocateSlicedTwoByteString(
substr_length, var_string.value(), var_from.value()));
Goto(&end);
Bind(&copy_routine);
Bind(&next);
}
// The subject string can only be external or sequential string of either
// encoding at this point.
STATIC_ASSERT(kExternalStringTag != 0);
STATIC_ASSERT(kSeqStringTag == 0);
GotoIfNot(Word32Equal(Word32And(var_instance_type.value(),
Int32Constant(kExternalStringTag)),
Int32Constant(0)),
&external_string);
GotoIf(to_direct.is_external(), &external_string);
var_result.Bind(AllocAndCopyStringCharacters(
this, context, var_string.value(), var_instance_type.value(),
var_from.value(), substr_length));
this, context, direct_string, instance_type, offset, substr_length));
Counters* counters = isolate()->counters();
IncrementCounter(counters->sub_string_native(), 1);
......@@ -3492,12 +3289,11 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
// Handle external string.
Bind(&external_string);
{
Node* const fake_sequential_string = TryDerefExternalString(
var_string.value(), var_instance_type.value(), &runtime);
Node* const fake_sequential_string = to_direct.PointerToString(&runtime);
var_result.Bind(AllocAndCopyStringCharacters(
this, context, fake_sequential_string, var_instance_type.value(),
var_from.value(), substr_length));
var_result.Bind(
AllocAndCopyStringCharacters(this, context, fake_sequential_string,
instance_type, offset, substr_length));
Counters* counters = isolate()->counters();
IncrementCounter(counters->sub_string_native(), 1);
......@@ -3508,7 +3304,7 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
// Substrings of length 1 are generated through CharCodeAt and FromCharCode.
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));
Goto(&end);
}
......@@ -3542,45 +3338,30 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
return var_result.value();
}
namespace {
Node* IsExternalStringInstanceType(CodeStubAssembler* a,
Node* const instance_type) {
CSA_ASSERT(a, a->IsStringInstanceType(instance_type));
return a->Word32Equal(
a->Word32And(instance_type, a->Int32Constant(kStringRepresentationMask)),
a->Int32Constant(kExternalStringTag));
}
ToDirectStringAssembler::ToDirectStringAssembler(
compiler::CodeAssemblerState* state, Node* string)
: CodeStubAssembler(state),
var_string_(this, MachineRepresentation::kTagged, string),
var_instance_type_(this, MachineRepresentation::kWord32),
var_offset_(this, MachineType::PointerRepresentation()),
var_is_external_(this, MachineRepresentation::kWord32) {
CSA_ASSERT(this, TaggedIsNotSmi(string));
CSA_ASSERT(this, IsString(string));
Node* IsShortExternalStringInstanceType(CodeStubAssembler* a,
Node* const instance_type) {
CSA_ASSERT(a, a->IsStringInstanceType(instance_type));
STATIC_ASSERT(kShortExternalStringTag != 0);
return a->Word32NotEqual(
a->Word32And(instance_type, a->Int32Constant(kShortExternalStringMask)),
a->Int32Constant(0));
var_string_.Bind(string);
var_offset_.Bind(IntPtrConstant(0));
var_instance_type_.Bind(LoadInstanceType(string));
var_is_external_.Bind(Int32Constant(0));
}
} // namespace
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());
Node* ToDirectStringAssembler::TryToDirect(Label* if_bailout) {
VariableList vars({&var_string_, &var_offset_, &var_instance_type_}, zone());
Label dispatch(this, vars);
Label if_isdirect(this);
Label if_iscons(this, Label::kDeferred);
Label if_isexternal(this, Label::kDeferred);
Label if_issliced(this, Label::kDeferred);
Label if_isthin(this, Label::kDeferred);
Label if_iscons(this);
Label if_isexternal(this);
Label if_issliced(this);
Label if_isthin(this);
Label out(this);
Goto(&dispatch);
......@@ -3592,12 +3373,12 @@ void CodeStubAssembler::TryUnpackString(Variable* var_string,
kSlicedStringTag, kThinStringTag,
};
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));
Node* const representation = Word32And(
var_instance_type->value(), Int32Constant(kStringRepresentationMask));
var_instance_type_.value(), Int32Constant(kStringRepresentationMask));
Switch(representation, if_bailout, values, labels, arraysize(values));
}
......@@ -3605,13 +3386,13 @@ void CodeStubAssembler::TryUnpackString(Variable* var_string,
// Flat cons strings have an empty second part.
Bind(&if_iscons);
{
Node* const string = var_string->value();
Node* const string = var_string_.value();
GotoIfNot(IsEmptyString(LoadObjectField(string, ConsString::kSecondOffset)),
if_bailout);
Node* const lhs = LoadObjectField(string, ConsString::kFirstOffset);
var_string->Bind(BitcastTaggedToWord(lhs));
var_instance_type->Bind(LoadInstanceType(lhs));
var_string_.Bind(lhs);
var_instance_type_.Bind(LoadInstanceType(lhs));
Goto(&dispatch);
}
......@@ -3619,14 +3400,14 @@ void CodeStubAssembler::TryUnpackString(Variable* var_string,
// Sliced string. Fetch parent and correct start index by offset.
Bind(&if_issliced);
{
Node* const string = var_string->value();
Node* const string = var_string_.value();
Node* const sliced_offset =
LoadObjectField(string, SlicedString::kOffsetOffset);
var_offset->Bind(IntPtrAdd(var_offset->value(), SmiUntag(sliced_offset)));
LoadAndUntagObjectField(string, SlicedString::kOffsetOffset);
var_offset_.Bind(IntPtrAdd(var_offset_.value(), sliced_offset));
Node* const parent = LoadObjectField(string, SlicedString::kParentOffset);
var_string->Bind(BitcastTaggedToWord(parent));
var_instance_type->Bind(LoadInstanceType(parent));
var_string_.Bind(parent);
var_instance_type_.Bind(LoadInstanceType(parent));
Goto(&dispatch);
}
......@@ -3634,36 +3415,65 @@ void CodeStubAssembler::TryUnpackString(Variable* var_string,
// Thin string. Fetch the actual string.
Bind(&if_isthin);
{
Node* const string = var_string->value();
Node* const string = var_string_.value();
Node* const actual_string =
LoadObjectField(string, ThinString::kActualOffset);
Node* const actual_instance_type = LoadInstanceType(actual_string);
var_string->Bind(BitcastTaggedToWord(actual_string));
var_instance_type->Bind(actual_instance_type);
var_string_.Bind(actual_string);
var_instance_type_.Bind(actual_instance_type);
Goto(&dispatch);
}
// External string.
Bind(&if_isexternal);
{
Node* const string = var_string->value();
Node* const faked_seq_string =
TryDerefExternalString(string, var_instance_type->value(), if_bailout);
var_is_external_.Bind(Int32Constant(1));
Goto(&out);
STATIC_ASSERT(kSeqStringTag == 0x0);
Node* const faked_seq_instance_type = Word32Xor(
var_instance_type->value(), Int32Constant(kExternalStringTag));
CSA_ASSERT(this, IsSequentialStringInstanceType(faked_seq_instance_type));
Bind(&out);
return var_string_.value();
}
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);
var_instance_type->Bind(faked_seq_instance_type);
Bind(&if_isexternal);
{
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,
......@@ -3671,9 +3481,8 @@ Node* CodeStubAssembler::TryDerefExternalString(Node* const string,
Label* if_bailout) {
Label out(this);
USE(IsExternalStringInstanceType);
CSA_ASSERT(this, IsExternalStringInstanceType(this, instance_type));
GotoIf(IsShortExternalStringInstanceType(this, instance_type), if_bailout);
CSA_ASSERT(this, IsExternalStringInstanceType(instance_type));
GotoIf(IsShortExternalStringInstanceType(instance_type), if_bailout);
// Move the pointer so that offset-wise, it looks like a sequential string.
STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
......
......@@ -692,6 +692,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* IsSpecialReceiverInstanceType(Node* instance_type);
Node* IsStringInstanceType(Node* instance_type);
Node* IsOneByteStringInstanceType(Node* instance_type);
Node* IsExternalStringInstanceType(Node* instance_type);
Node* IsShortExternalStringInstanceType(Node* instance_type);
Node* IsSequentialStringInstanceType(Node* instance_type);
Node* IsString(Node* object);
Node* IsJSObject(Node* object);
......@@ -738,14 +740,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* StringAdd(Node* context, Node* first, Node* second,
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
// like a sequential string.
// Note that this pointer is not tagged and does not point to a real
......@@ -1399,6 +1393,42 @@ class CodeStubArguments {
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
#define CSA_ASSERT(csa, x) \
(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