Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(682)

Unified Diff: src/code-stub-assembler.cc

Issue 2549773002: Internalize strings in-place (Closed)
Patch Set: rebased Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/code-stub-assembler.h ('k') | src/code-stubs.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/code-stub-assembler.cc
diff --git a/src/code-stub-assembler.cc b/src/code-stub-assembler.cc
index 4a96e81ebf843ed9848a40311de0c7b36e4127c6..2469e2cc4871154fe57fe0973bc6c63118491c4c 100644
--- a/src/code-stub-assembler.cc
+++ b/src/code-stub-assembler.cc
@@ -1572,6 +1572,9 @@ Node* CodeStubAssembler::AllocateHeapNumberWithValue(Node* value,
Node* CodeStubAssembler::AllocateSeqOneByteString(int length,
AllocationFlags flags) {
Comment("AllocateSeqOneByteString");
+ if (length == 0) {
+ return LoadRoot(Heap::kempty_stringRootIndex);
+ }
Node* result = Allocate(SeqOneByteString::SizeFor(length), flags);
DCHECK(Heap::RootIsImmortalImmovable(Heap::kOneByteStringMapRootIndex));
StoreMapNoWriteBarrier(result, Heap::kOneByteStringMapRootIndex);
@@ -1591,8 +1594,10 @@ Node* CodeStubAssembler::AllocateSeqOneByteString(Node* context, Node* length,
Variable var_result(this, MachineRepresentation::kTagged);
// Compute the SeqOneByteString size and check if it fits into new space.
- Label if_sizeissmall(this), if_notsizeissmall(this, Label::kDeferred),
- if_join(this);
+ Label if_lengthiszero(this), if_sizeissmall(this),
+ if_notsizeissmall(this, Label::kDeferred), if_join(this);
+ GotoIf(WordEqual(length, IntPtrOrSmiConstant(0, mode)), &if_lengthiszero);
+
Node* raw_size = GetArrayAllocationSize(
length, UINT8_ELEMENTS, mode,
SeqOneByteString::kHeaderSize + kObjectAlignmentMask);
@@ -1625,6 +1630,12 @@ Node* CodeStubAssembler::AllocateSeqOneByteString(Node* context, Node* length,
Goto(&if_join);
}
+ Bind(&if_lengthiszero);
+ {
+ var_result.Bind(LoadRoot(Heap::kempty_stringRootIndex));
+ Goto(&if_join);
+ }
+
Bind(&if_join);
return var_result.value();
}
@@ -1632,6 +1643,9 @@ Node* CodeStubAssembler::AllocateSeqOneByteString(Node* context, Node* length,
Node* CodeStubAssembler::AllocateSeqTwoByteString(int length,
AllocationFlags flags) {
Comment("AllocateSeqTwoByteString");
+ if (length == 0) {
+ return LoadRoot(Heap::kempty_stringRootIndex);
+ }
Node* result = Allocate(SeqTwoByteString::SizeFor(length), flags);
DCHECK(Heap::RootIsImmortalImmovable(Heap::kStringMapRootIndex));
StoreMapNoWriteBarrier(result, Heap::kStringMapRootIndex);
@@ -1651,8 +1665,10 @@ Node* CodeStubAssembler::AllocateSeqTwoByteString(Node* context, Node* length,
Variable var_result(this, MachineRepresentation::kTagged);
// Compute the SeqTwoByteString size and check if it fits into new space.
- Label if_sizeissmall(this), if_notsizeissmall(this, Label::kDeferred),
- if_join(this);
+ Label if_lengthiszero(this), if_sizeissmall(this),
+ if_notsizeissmall(this, Label::kDeferred), if_join(this);
+ GotoIf(WordEqual(length, IntPtrOrSmiConstant(0, mode)), &if_lengthiszero);
+
Node* raw_size = GetArrayAllocationSize(
length, UINT16_ELEMENTS, mode,
SeqOneByteString::kHeaderSize + kObjectAlignmentMask);
@@ -1687,6 +1703,12 @@ Node* CodeStubAssembler::AllocateSeqTwoByteString(Node* context, Node* length,
Goto(&if_join);
}
+ Bind(&if_lengthiszero);
+ {
+ var_result.Bind(LoadRoot(Heap::kempty_stringRootIndex));
+ Goto(&if_join);
+ }
+
Bind(&if_join);
return var_result.value();
}
@@ -3002,7 +3024,7 @@ Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index,
// Translate the {index} into a Word.
index = ParameterToWord(index, parameter_mode);
- // We may need to loop in case of cons or sliced strings.
+ // We may need to loop in case of cons, thin, or sliced strings.
Variable var_index(this, MachineType::PointerRepresentation());
Variable var_result(this, MachineRepresentation::kWord32);
Variable var_string(this, MachineRepresentation::kTagged);
@@ -3154,14 +3176,29 @@ Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index,
Bind(&if_stringisnotexternal);
{
- // 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);
+ 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);
+ }
}
}
}
@@ -3292,11 +3329,13 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
Label runtime(this);
Variable var_instance_type(this, MachineRepresentation::kWord32); // Int32.
+ Variable var_representation(this, MachineRepresentation::kWord32); // Int32.
Variable var_result(this, MachineRepresentation::kTagged); // String.
Variable var_from(this, MachineRepresentation::kTagged); // Smi.
Variable var_string(this, MachineRepresentation::kTagged); // String.
var_instance_type.Bind(Int32Constant(0));
+ var_representation.Bind(Int32Constant(0));
var_string.Bind(string);
var_from.Bind(from);
@@ -3337,7 +3376,8 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
// 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));
+ STATIC_ASSERT(kIsIndirectStringMask ==
+ (kSlicedStringTag & kConsStringTag & kThinStringTag));
STATIC_ASSERT(kIsIndirectStringMask != 0);
Label underlying_unpacked(this);
GotoIf(Word32Equal(
@@ -3345,13 +3385,14 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
Int32Constant(0)),
&underlying_unpacked);
- // The subject string is either a sliced or cons string.
+ // The subject string is a sliced, cons, or thin string.
- Label sliced_string(this);
- GotoIf(Word32NotEqual(
- Word32And(instance_type, Int32Constant(kSlicedNotConsMask)),
- Int32Constant(0)),
- &sliced_string);
+ Label sliced_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.
// Flat cons strings have an empty second part.
@@ -3363,14 +3404,33 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
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_or_sliced);
+ }
+
+ Bind(&thin_or_sliced);
+ {
+ GotoIf(Word32Equal(var_representation.value(),
+ Int32Constant(kSlicedStringTag)),
+ &sliced_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);
}
Bind(&sliced_string);
{
// Fetch parent and correct start index by offset.
- Node* sliced_offset = LoadObjectField(string, SlicedString::kOffsetOffset);
+ Node* sliced_offset =
+ LoadObjectField(var_string.value(), SlicedString::kOffsetOffset);
var_from.Bind(SmiAdd(from, sliced_offset));
Node* slice_parent = LoadObjectField(string, SlicedString::kParentOffset);
@@ -3509,12 +3569,49 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
return var_result.value();
}
+void CodeStubAssembler::MaybeDerefIndirectString(Variable* var_string,
+ Node* instance_type,
+ Variable* var_did_something) {
+ Label deref(this), done(this, var_did_something);
+ Node* representation =
+ Word32And(instance_type, Int32Constant(kStringRepresentationMask));
+ GotoIf(Word32Equal(representation, Int32Constant(kThinStringTag)), &deref);
+ GotoIf(Word32NotEqual(representation, Int32Constant(kConsStringTag)), &done);
+ // Cons string.
+ Node* rhs = LoadObjectField(var_string->value(), ConsString::kSecondOffset);
+ GotoIf(WordEqual(rhs, EmptyStringConstant()), &deref);
+ Goto(&done);
+
+ Bind(&deref);
+ STATIC_ASSERT(ThinString::kActualOffset == ConsString::kFirstOffset);
+ var_string->Bind(
+ LoadObjectField(var_string->value(), ThinString::kActualOffset));
+ var_did_something->Bind(IntPtrConstant(1));
+ Goto(&done);
+
+ Bind(&done);
+}
+
+void CodeStubAssembler::MaybeDerefIndirectStrings(Variable* var_left,
+ Node* left_instance_type,
+ Variable* var_right,
+ Node* right_instance_type,
+ Label* did_something) {
+ Variable var_did_something(this, MachineType::PointerRepresentation());
+ var_did_something.Bind(IntPtrConstant(0));
+ MaybeDerefIndirectString(var_left, left_instance_type, &var_did_something);
+ MaybeDerefIndirectString(var_right, right_instance_type, &var_did_something);
+
+ GotoIf(WordNotEqual(var_did_something.value(), IntPtrConstant(0)),
+ did_something);
+ // Fall through if neither string was an indirect string.
+}
+
Node* CodeStubAssembler::StringAdd(Node* context, Node* left, Node* right,
AllocationFlags flags) {
Label check_right(this);
Label runtime(this, Label::kDeferred);
Label cons(this);
- Label non_cons(this);
Variable result(this, MachineRepresentation::kTagged);
Label done(this, &result);
Label done_native(this, &result);
@@ -3532,72 +3629,92 @@ Node* CodeStubAssembler::StringAdd(Node* context, Node* left, Node* right,
Goto(&done_native);
Bind(&cons);
- CSA_ASSERT(this, TaggedIsSmi(left_length));
- CSA_ASSERT(this, TaggedIsSmi(right_length));
- Node* new_length = SmiAdd(left_length, right_length);
- GotoIf(SmiAboveOrEqual(new_length, SmiConstant(String::kMaxLength)),
- &runtime);
-
- GotoIf(SmiLessThan(new_length, SmiConstant(ConsString::kMinLength)),
- &non_cons);
-
- result.Bind(NewConsString(context, new_length, left, right, flags));
- Goto(&done_native);
-
- Bind(&non_cons);
+ {
+ CSA_ASSERT(this, TaggedIsSmi(left_length));
+ CSA_ASSERT(this, TaggedIsSmi(right_length));
+ Node* new_length = SmiAdd(left_length, right_length);
+ GotoIf(SmiAboveOrEqual(new_length, SmiConstant(String::kMaxLength)),
+ &runtime);
- Comment("Full string concatenate");
- Node* left_instance_type = LoadInstanceType(left);
- Node* right_instance_type = LoadInstanceType(right);
- // Compute intersection and difference of instance types.
+ Variable var_left(this, MachineRepresentation::kTagged);
+ Variable var_right(this, MachineRepresentation::kTagged);
+ Variable* input_vars[2] = {&var_left, &var_right};
+ Label non_cons(this, 2, input_vars);
+ Label slow(this, Label::kDeferred);
+ var_left.Bind(left);
+ var_right.Bind(right);
+ GotoIf(SmiLessThan(new_length, SmiConstant(ConsString::kMinLength)),
+ &non_cons);
+
+ result.Bind(NewConsString(context, new_length, var_left.value(),
+ var_right.value(), flags));
+ Goto(&done_native);
- Node* ored_instance_types = Word32Or(left_instance_type, right_instance_type);
- Node* xored_instance_types =
- Word32Xor(left_instance_type, right_instance_type);
+ Bind(&non_cons);
- // Check if both strings have the same encoding and both are sequential.
- GotoIf(Word32NotEqual(Word32And(xored_instance_types,
- Int32Constant(kStringEncodingMask)),
- Int32Constant(0)),
- &runtime);
- GotoIf(Word32NotEqual(Word32And(ored_instance_types,
- Int32Constant(kStringRepresentationMask)),
- Int32Constant(0)),
- &runtime);
+ Comment("Full string concatenate");
+ Node* left_instance_type = LoadInstanceType(var_left.value());
+ Node* right_instance_type = LoadInstanceType(var_right.value());
+ // Compute intersection and difference of instance types.
- Label two_byte(this);
- GotoIf(Word32Equal(
- Word32And(ored_instance_types, Int32Constant(kStringEncodingMask)),
- Int32Constant(kTwoByteStringTag)),
- &two_byte);
- // One-byte sequential string case
- Node* new_string =
- AllocateSeqOneByteString(context, new_length, SMI_PARAMETERS);
- CopyStringCharacters(left, new_string, SmiConstant(Smi::kZero),
- SmiConstant(Smi::kZero), left_length,
- String::ONE_BYTE_ENCODING, String::ONE_BYTE_ENCODING,
- SMI_PARAMETERS);
- CopyStringCharacters(right, new_string, SmiConstant(Smi::kZero), left_length,
- right_length, String::ONE_BYTE_ENCODING,
- String::ONE_BYTE_ENCODING, SMI_PARAMETERS);
- result.Bind(new_string);
- Goto(&done_native);
+ Node* ored_instance_types =
+ Word32Or(left_instance_type, right_instance_type);
+ Node* xored_instance_types =
+ Word32Xor(left_instance_type, right_instance_type);
- Bind(&two_byte);
- {
- // Two-byte sequential string case
- new_string = AllocateSeqTwoByteString(context, new_length, SMI_PARAMETERS);
- CopyStringCharacters(left, new_string, SmiConstant(Smi::kZero),
+ // Check if both strings have the same encoding and both are sequential.
+ GotoIf(Word32NotEqual(Word32And(xored_instance_types,
+ Int32Constant(kStringEncodingMask)),
+ Int32Constant(0)),
+ &runtime);
+ GotoIf(Word32NotEqual(Word32And(ored_instance_types,
+ Int32Constant(kStringRepresentationMask)),
+ Int32Constant(0)),
+ &slow);
+
+ Label two_byte(this);
+ GotoIf(Word32Equal(Word32And(ored_instance_types,
+ Int32Constant(kStringEncodingMask)),
+ Int32Constant(kTwoByteStringTag)),
+ &two_byte);
+ // One-byte sequential string case
+ Node* new_string =
+ AllocateSeqOneByteString(context, new_length, SMI_PARAMETERS);
+ CopyStringCharacters(var_left.value(), new_string, SmiConstant(Smi::kZero),
SmiConstant(Smi::kZero), left_length,
- String::TWO_BYTE_ENCODING, String::TWO_BYTE_ENCODING,
+ String::ONE_BYTE_ENCODING, String::ONE_BYTE_ENCODING,
SMI_PARAMETERS);
- CopyStringCharacters(right, new_string, SmiConstant(Smi::kZero),
- left_length, right_length, String::TWO_BYTE_ENCODING,
- String::TWO_BYTE_ENCODING, SMI_PARAMETERS);
+ CopyStringCharacters(var_right.value(), new_string, SmiConstant(Smi::kZero),
+ left_length, right_length, String::ONE_BYTE_ENCODING,
+ String::ONE_BYTE_ENCODING, SMI_PARAMETERS);
result.Bind(new_string);
Goto(&done_native);
- }
+ Bind(&two_byte);
+ {
+ // Two-byte sequential string case
+ new_string =
+ AllocateSeqTwoByteString(context, new_length, SMI_PARAMETERS);
+ CopyStringCharacters(var_left.value(), new_string,
+ SmiConstant(Smi::kZero), SmiConstant(Smi::kZero),
+ left_length, String::TWO_BYTE_ENCODING,
+ String::TWO_BYTE_ENCODING, SMI_PARAMETERS);
+ CopyStringCharacters(var_right.value(), new_string,
+ SmiConstant(Smi::kZero), left_length, right_length,
+ String::TWO_BYTE_ENCODING, String::TWO_BYTE_ENCODING,
+ SMI_PARAMETERS);
+ result.Bind(new_string);
+ Goto(&done_native);
+ }
+
+ Bind(&slow);
+ {
+ // Try to unwrap indirect strings, restart the above attempt on success.
+ MaybeDerefIndirectStrings(&var_left, left_instance_type, &var_right,
+ right_instance_type, &non_cons);
+ Goto(&runtime);
+ }
+ }
Bind(&runtime);
{
result.Bind(CallRuntime(Runtime::kStringAdd, context, left, right));
@@ -4143,45 +4260,6 @@ Node* CodeStubAssembler::ToString(Node* context, Node* input) {
return result.value();
}
-Node* CodeStubAssembler::FlattenString(Node* string) {
- CSA_ASSERT(this, IsString(string));
- Variable var_result(this, MachineRepresentation::kTagged);
- var_result.Bind(string);
-
- Node* instance_type = LoadInstanceType(string);
-
- // Check if the {string} is not a ConsString (i.e. already flat).
- Label is_cons(this, Label::kDeferred), is_flat_in_cons(this), end(this);
- {
- GotoUnless(Word32Equal(Word32And(instance_type,
- Int32Constant(kStringRepresentationMask)),
- Int32Constant(kConsStringTag)),
- &end);
-
- // Check whether the right hand side is the empty string (i.e. if
- // this is really a flat string in a cons string).
- Node* rhs = LoadObjectField(string, ConsString::kSecondOffset);
- Branch(WordEqual(rhs, EmptyStringConstant()), &is_flat_in_cons, &is_cons);
- }
-
- // Bail out to the runtime.
- Bind(&is_cons);
- {
- var_result.Bind(
- CallRuntime(Runtime::kFlattenString, NoContextConstant(), string));
- Goto(&end);
- }
-
- Bind(&is_flat_in_cons);
- {
- var_result.Bind(LoadObjectField(string, ConsString::kFirstOffset));
- Goto(&end);
- }
-
- Bind(&end);
- return var_result.value();
-}
-
Node* CodeStubAssembler::JSReceiverToPrimitive(Node* context, Node* input) {
Label if_isreceiver(this, Label::kDeferred), if_isnotreceiver(this);
Variable result(this, MachineRepresentation::kTagged);
@@ -4323,17 +4401,19 @@ void CodeStubAssembler::Use(Label* label) {
void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex,
Variable* var_index, Label* if_keyisunique,
- Label* if_bailout) {
+ Variable* var_unique, Label* if_bailout) {
DCHECK_EQ(MachineType::PointerRepresentation(), var_index->rep());
+ DCHECK_EQ(MachineRepresentation::kTagged, var_unique->rep());
Comment("TryToName");
- Label if_hascachedindex(this), if_keyisnotindex(this);
+ Label if_hascachedindex(this), if_keyisnotindex(this), if_thinstring(this);
// Handle Smi and HeapNumber keys.
var_index->Bind(TryToIntptr(key, &if_keyisnotindex));
Goto(if_keyisindex);
Bind(&if_keyisnotindex);
Node* key_map = LoadMap(key);
+ var_unique->Bind(key);
// Symbols are unique.
GotoIf(IsSymbolMap(key_map), if_keyisunique);
Node* key_instance_type = LoadMapInstanceType(key_map);
@@ -4350,6 +4430,12 @@ void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex,
Node* not_an_index =
Word32And(hash, Int32Constant(Name::kIsNotArrayIndexMask));
GotoIf(Word32Equal(not_an_index, Int32Constant(0)), if_bailout);
+ // Check if we have a ThinString.
+ GotoIf(Word32Equal(key_instance_type, Int32Constant(THIN_STRING_TYPE)),
+ &if_thinstring);
+ GotoIf(
+ Word32Equal(key_instance_type, Int32Constant(THIN_ONE_BYTE_STRING_TYPE)),
+ &if_thinstring);
// Finally, check if |key| is internalized.
STATIC_ASSERT(kNotInternalizedTag != 0);
Node* not_internalized =
@@ -4357,6 +4443,10 @@ void CodeStubAssembler::TryToName(Node* key, Label* if_keyisindex,
GotoIf(Word32NotEqual(not_internalized, Int32Constant(0)), if_bailout);
Goto(if_keyisunique);
+ Bind(&if_thinstring);
+ var_unique->Bind(LoadObjectField(key, ThinString::kActualOffset));
+ Goto(if_keyisunique);
+
Bind(&if_hascachedindex);
var_index->Bind(DecodeWordFromWord32<Name::ArrayIndexValueBits>(hash));
Goto(if_keyisindex);
@@ -5206,9 +5296,11 @@ void CodeStubAssembler::TryPrototypeChainLookup(
}
Variable var_index(this, MachineType::PointerRepresentation());
+ Variable var_unique(this, MachineRepresentation::kTagged);
Label if_keyisindex(this), if_iskeyunique(this);
- TryToName(key, &if_keyisindex, &var_index, &if_iskeyunique, if_bailout);
+ TryToName(key, &if_keyisindex, &var_index, &if_iskeyunique, &var_unique,
+ if_bailout);
Bind(&if_iskeyunique);
{
@@ -5230,8 +5322,8 @@ void CodeStubAssembler::TryPrototypeChainLookup(
Label next_proto(this);
lookup_property_in_holder(receiver, var_holder.value(), holder_map,
- holder_instance_type, key, &next_proto,
- if_bailout);
+ holder_instance_type, var_unique.value(),
+ &next_proto, if_bailout);
Bind(&next_proto);
// Bailout if it can be an integer indexed exotic case.
« no previous file with comments | « src/code-stub-assembler.h ('k') | src/code-stubs.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698