[cleanup] Move many builtin String related functions out of CSA
Moves the following functions only used in string builtins out of CodeStubAssembler: StringAdd AllocateConsString StringFromSingleUTF16EncodedCodePoint BranchIfCanDerefIndirectString DerefIndirectString MaybeDerefIndirectString MaybeDerefIndirectStrings Bug: v8:9396 Change-Id: Ib89966b9c170ca23dd7535a0f550c69966a6e21c Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1817608 Commit-Queue: Dan Elphick <delphick@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Cr-Commit-Position: refs/heads/master@{#63932}
This commit is contained in:
parent
3616229f12
commit
33ff3b05a3
@ -10,6 +10,7 @@
|
||||
#include "src/codegen/code-factory.h"
|
||||
#include "src/heap/factory-inl.h"
|
||||
#include "src/heap/heap-inl.h"
|
||||
#include "src/logging/counters.h"
|
||||
#include "src/objects/objects.h"
|
||||
#include "src/objects/property-cell.h"
|
||||
|
||||
@ -287,6 +288,262 @@ void StringBuiltinsAssembler::StringEqual_Loop(
|
||||
}
|
||||
}
|
||||
|
||||
TNode<String> StringBuiltinsAssembler::StringFromSingleUTF16EncodedCodePoint(
|
||||
TNode<Int32T> codepoint) {
|
||||
VARIABLE(var_result, MachineRepresentation::kTagged, 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(StringFromSingleCharCode(codepoint));
|
||||
Goto(&return_result);
|
||||
}
|
||||
|
||||
BIND(&if_isword32);
|
||||
{
|
||||
TNode<String> value = AllocateSeqTwoByteString(2);
|
||||
StoreNoWriteBarrier(
|
||||
MachineRepresentation::kWord32, value,
|
||||
IntPtrConstant(SeqTwoByteString::kHeaderSize - kHeapObjectTag),
|
||||
codepoint);
|
||||
var_result.Bind(value);
|
||||
Goto(&return_result);
|
||||
}
|
||||
|
||||
BIND(&return_result);
|
||||
return CAST(var_result.value());
|
||||
}
|
||||
|
||||
TNode<String> StringBuiltinsAssembler::AllocateConsString(TNode<Uint32T> length,
|
||||
TNode<String> left,
|
||||
TNode<String> right) {
|
||||
// Added string can be a cons string.
|
||||
Comment("Allocating ConsString");
|
||||
TNode<Int32T> left_instance_type = LoadInstanceType(left);
|
||||
TNode<Int32T> right_instance_type = LoadInstanceType(right);
|
||||
|
||||
// Determine the resulting ConsString map to use depending on whether
|
||||
// any of {left} or {right} has two byte encoding.
|
||||
STATIC_ASSERT(kOneByteStringTag != 0);
|
||||
STATIC_ASSERT(kTwoByteStringTag == 0);
|
||||
TNode<Int32T> combined_instance_type =
|
||||
Word32And(left_instance_type, right_instance_type);
|
||||
TNode<Map> result_map = CAST(Select<Object>(
|
||||
IsSetWord32(combined_instance_type, kStringEncodingMask),
|
||||
[=] { return ConsOneByteStringMapConstant(); },
|
||||
[=] { return ConsStringMapConstant(); }));
|
||||
TNode<HeapObject> result = AllocateInNewSpace(ConsString::kSize);
|
||||
StoreMapNoWriteBarrier(result, result_map);
|
||||
StoreObjectFieldNoWriteBarrier(result, ConsString::kLengthOffset, length,
|
||||
MachineRepresentation::kWord32);
|
||||
StoreObjectFieldNoWriteBarrier(result, ConsString::kHashFieldOffset,
|
||||
Int32Constant(String::kEmptyHashField),
|
||||
MachineRepresentation::kWord32);
|
||||
StoreObjectFieldNoWriteBarrier(result, ConsString::kFirstOffset, left);
|
||||
StoreObjectFieldNoWriteBarrier(result, ConsString::kSecondOffset, right);
|
||||
return CAST(result);
|
||||
}
|
||||
|
||||
TNode<String> StringBuiltinsAssembler::StringAdd(Node* context,
|
||||
TNode<String> left,
|
||||
TNode<String> right) {
|
||||
TVARIABLE(String, result);
|
||||
Label check_right(this), runtime(this, Label::kDeferred), cons(this),
|
||||
done(this, &result), done_native(this, &result);
|
||||
Counters* counters = isolate()->counters();
|
||||
|
||||
TNode<Uint32T> left_length = LoadStringLengthAsWord32(left);
|
||||
GotoIfNot(Word32Equal(left_length, Uint32Constant(0)), &check_right);
|
||||
result = right;
|
||||
Goto(&done_native);
|
||||
|
||||
BIND(&check_right);
|
||||
TNode<Uint32T> right_length = LoadStringLengthAsWord32(right);
|
||||
GotoIfNot(Word32Equal(right_length, Uint32Constant(0)), &cons);
|
||||
result = left;
|
||||
Goto(&done_native);
|
||||
|
||||
BIND(&cons);
|
||||
{
|
||||
TNode<Uint32T> new_length = Uint32Add(left_length, right_length);
|
||||
|
||||
// If new length is greater than String::kMaxLength, goto runtime to
|
||||
// throw. Note: we also need to invalidate the string length protector, so
|
||||
// can't just throw here directly.
|
||||
GotoIf(Uint32GreaterThan(new_length, Uint32Constant(String::kMaxLength)),
|
||||
&runtime);
|
||||
|
||||
TVARIABLE(String, var_left, left);
|
||||
TVARIABLE(String, var_right, right);
|
||||
Variable* input_vars[2] = {&var_left, &var_right};
|
||||
Label non_cons(this, 2, input_vars);
|
||||
Label slow(this, Label::kDeferred);
|
||||
GotoIf(Uint32LessThan(new_length, Uint32Constant(ConsString::kMinLength)),
|
||||
&non_cons);
|
||||
|
||||
result =
|
||||
AllocateConsString(new_length, var_left.value(), var_right.value());
|
||||
Goto(&done_native);
|
||||
|
||||
BIND(&non_cons);
|
||||
|
||||
Comment("Full string concatenate");
|
||||
TNode<Int32T> left_instance_type = LoadInstanceType(var_left.value());
|
||||
TNode<Int32T> right_instance_type = LoadInstanceType(var_right.value());
|
||||
// Compute intersection and difference of instance types.
|
||||
|
||||
TNode<Int32T> ored_instance_types =
|
||||
Word32Or(left_instance_type, right_instance_type);
|
||||
TNode<Word32T> xored_instance_types =
|
||||
Word32Xor(left_instance_type, right_instance_type);
|
||||
|
||||
// Check if both strings have the same encoding and both are sequential.
|
||||
GotoIf(IsSetWord32(xored_instance_types, kStringEncodingMask), &runtime);
|
||||
GotoIf(IsSetWord32(ored_instance_types, kStringRepresentationMask), &slow);
|
||||
|
||||
TNode<IntPtrT> word_left_length = Signed(ChangeUint32ToWord(left_length));
|
||||
TNode<IntPtrT> word_right_length = Signed(ChangeUint32ToWord(right_length));
|
||||
|
||||
Label two_byte(this);
|
||||
GotoIf(Word32Equal(Word32And(ored_instance_types,
|
||||
Int32Constant(kStringEncodingMask)),
|
||||
Int32Constant(kTwoByteStringTag)),
|
||||
&two_byte);
|
||||
// One-byte sequential string case
|
||||
result = AllocateSeqOneByteString(new_length);
|
||||
CopyStringCharacters(var_left.value(), result.value(), IntPtrConstant(0),
|
||||
IntPtrConstant(0), word_left_length,
|
||||
String::ONE_BYTE_ENCODING, String::ONE_BYTE_ENCODING);
|
||||
CopyStringCharacters(var_right.value(), result.value(), IntPtrConstant(0),
|
||||
word_left_length, word_right_length,
|
||||
String::ONE_BYTE_ENCODING, String::ONE_BYTE_ENCODING);
|
||||
Goto(&done_native);
|
||||
|
||||
BIND(&two_byte);
|
||||
{
|
||||
// Two-byte sequential string case
|
||||
result = AllocateSeqTwoByteString(new_length);
|
||||
CopyStringCharacters(var_left.value(), result.value(), IntPtrConstant(0),
|
||||
IntPtrConstant(0), word_left_length,
|
||||
String::TWO_BYTE_ENCODING,
|
||||
String::TWO_BYTE_ENCODING);
|
||||
CopyStringCharacters(var_right.value(), result.value(), IntPtrConstant(0),
|
||||
word_left_length, word_right_length,
|
||||
String::TWO_BYTE_ENCODING,
|
||||
String::TWO_BYTE_ENCODING);
|
||||
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 = CAST(CallRuntime(Runtime::kStringAdd, context, left, right));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&done_native);
|
||||
{
|
||||
IncrementCounter(counters->string_add_native(), 1);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&done);
|
||||
return result.value();
|
||||
}
|
||||
|
||||
void StringBuiltinsAssembler::BranchIfCanDerefIndirectString(
|
||||
TNode<String> string, TNode<Int32T> instance_type, Label* can_deref,
|
||||
Label* cannot_deref) {
|
||||
TNode<Int32T> representation =
|
||||
Word32And(instance_type, Int32Constant(kStringRepresentationMask));
|
||||
GotoIf(Word32Equal(representation, Int32Constant(kThinStringTag)), can_deref);
|
||||
GotoIf(Word32NotEqual(representation, Int32Constant(kConsStringTag)),
|
||||
cannot_deref);
|
||||
// Cons string.
|
||||
TNode<String> rhs =
|
||||
LoadObjectField<String>(string, ConsString::kSecondOffset);
|
||||
GotoIf(IsEmptyString(rhs), can_deref);
|
||||
Goto(cannot_deref);
|
||||
}
|
||||
|
||||
void StringBuiltinsAssembler::DerefIndirectString(TVariable<String>* var_string,
|
||||
TNode<Int32T> instance_type) {
|
||||
#ifdef DEBUG
|
||||
Label can_deref(this), cannot_deref(this);
|
||||
BranchIfCanDerefIndirectString(var_string->value(), instance_type, &can_deref,
|
||||
&cannot_deref);
|
||||
BIND(&cannot_deref);
|
||||
DebugBreak(); // Should be able to dereference string.
|
||||
Goto(&can_deref);
|
||||
BIND(&can_deref);
|
||||
#endif // DEBUG
|
||||
|
||||
STATIC_ASSERT(static_cast<int>(ThinString::kActualOffset) ==
|
||||
static_cast<int>(ConsString::kFirstOffset));
|
||||
*var_string =
|
||||
LoadObjectField<String>(var_string->value(), ThinString::kActualOffset);
|
||||
}
|
||||
|
||||
void StringBuiltinsAssembler::MaybeDerefIndirectString(
|
||||
TVariable<String>* var_string, TNode<Int32T> instance_type,
|
||||
Label* did_deref, Label* cannot_deref) {
|
||||
Label deref(this);
|
||||
BranchIfCanDerefIndirectString(var_string->value(), instance_type, &deref,
|
||||
cannot_deref);
|
||||
|
||||
BIND(&deref);
|
||||
{
|
||||
DerefIndirectString(var_string, instance_type);
|
||||
Goto(did_deref);
|
||||
}
|
||||
}
|
||||
|
||||
void StringBuiltinsAssembler::MaybeDerefIndirectStrings(
|
||||
TVariable<String>* var_left, TNode<Int32T> left_instance_type,
|
||||
TVariable<String>* var_right, TNode<Int32T> right_instance_type,
|
||||
Label* did_something) {
|
||||
Label did_nothing_left(this), did_something_left(this),
|
||||
didnt_do_anything(this);
|
||||
MaybeDerefIndirectString(var_left, left_instance_type, &did_something_left,
|
||||
&did_nothing_left);
|
||||
|
||||
BIND(&did_something_left);
|
||||
{
|
||||
MaybeDerefIndirectString(var_right, right_instance_type, did_something,
|
||||
did_something);
|
||||
}
|
||||
|
||||
BIND(&did_nothing_left);
|
||||
{
|
||||
MaybeDerefIndirectString(var_right, right_instance_type, did_something,
|
||||
&didnt_do_anything);
|
||||
}
|
||||
|
||||
BIND(&didnt_do_anything);
|
||||
// Fall through if neither string was an indirect string.
|
||||
}
|
||||
|
||||
TNode<String> StringBuiltinsAssembler::DerefIndirectString(
|
||||
TNode<String> string, TNode<Int32T> instance_type, Label* cannot_deref) {
|
||||
Label deref(this);
|
||||
BranchIfCanDerefIndirectString(string, instance_type, &deref, cannot_deref);
|
||||
BIND(&deref);
|
||||
STATIC_ASSERT(static_cast<int>(ThinString::kActualOffset) ==
|
||||
static_cast<int>(ConsString::kFirstOffset));
|
||||
return LoadObjectField<String>(string, ThinString::kActualOffset);
|
||||
}
|
||||
|
||||
TF_BUILTIN(StringAdd_CheckNone, StringBuiltinsAssembler) {
|
||||
TNode<String> left = CAST(Parameter(Descriptor::kLeft));
|
||||
TNode<String> right = CAST(Parameter(Descriptor::kRight));
|
||||
|
@ -33,6 +33,8 @@ class StringBuiltinsAssembler : public CodeStubAssembler {
|
||||
SloppyTNode<IntPtrT> index,
|
||||
UnicodeEncoding encoding);
|
||||
|
||||
TNode<String> StringFromSingleUTF16EncodedCodePoint(TNode<Int32T> codepoint);
|
||||
|
||||
protected:
|
||||
void StringEqual_Loop(Node* lhs, Node* lhs_instance_type,
|
||||
MachineType lhs_type, Node* rhs,
|
||||
@ -82,6 +84,38 @@ class StringBuiltinsAssembler : public CodeStubAssembler {
|
||||
return SmiLessThan(value, SmiConstant(0));
|
||||
}
|
||||
|
||||
TNode<String> AllocateConsString(TNode<Uint32T> length, TNode<String> left,
|
||||
TNode<String> right);
|
||||
|
||||
TNode<String> StringAdd(Node* context, TNode<String> left,
|
||||
TNode<String> right);
|
||||
|
||||
// Check if |string| is an indirect (thin or flat cons) string type that can
|
||||
// be dereferenced by DerefIndirectString.
|
||||
void BranchIfCanDerefIndirectString(TNode<String> string,
|
||||
TNode<Int32T> instance_type,
|
||||
Label* can_deref, Label* cannot_deref);
|
||||
// Allocate an appropriate one- or two-byte ConsString with the first and
|
||||
// second parts specified by |left| and |right|.
|
||||
// Unpack an indirect (thin or flat cons) string type.
|
||||
void DerefIndirectString(TVariable<String>* var_string,
|
||||
TNode<Int32T> instance_type);
|
||||
// Check if |var_string| has an indirect (thin or flat cons) string type, and
|
||||
// unpack it if so.
|
||||
void MaybeDerefIndirectString(TVariable<String>* var_string,
|
||||
TNode<Int32T> instance_type, Label* did_deref,
|
||||
Label* cannot_deref);
|
||||
// Check if |var_left| or |var_right| has an indirect (thin or flat cons)
|
||||
// string type, and unpack it/them if so. Fall through if nothing was done.
|
||||
void MaybeDerefIndirectStrings(TVariable<String>* var_left,
|
||||
TNode<Int32T> left_instance_type,
|
||||
TVariable<String>* var_right,
|
||||
TNode<Int32T> right_instance_type,
|
||||
Label* did_something);
|
||||
TNode<String> DerefIndirectString(TNode<String> string,
|
||||
TNode<Int32T> instance_type,
|
||||
Label* cannot_deref);
|
||||
|
||||
// Implements boilerplate logic for {match, split, replace, search} of the
|
||||
// form:
|
||||
//
|
||||
|
@ -21,7 +21,8 @@ namespace string {
|
||||
|
||||
extern macro StringBuiltinsAssembler::LoadSurrogatePairAt(
|
||||
String, intptr, intptr, constexpr UnicodeEncoding): int32;
|
||||
extern macro StringFromSingleUTF16EncodedCodePoint(int32): String;
|
||||
extern macro StringBuiltinsAssembler::StringFromSingleUTF16EncodedCodePoint(
|
||||
int32): String;
|
||||
|
||||
// This function assumes StringPrimitiveWithNoCustomIteration is true.
|
||||
transitioning builtin StringToList(implicit context: Context)(string: String):
|
||||
|
@ -3588,35 +3588,6 @@ TNode<String> CodeStubAssembler::AllocateSlicedTwoByteString(
|
||||
offset);
|
||||
}
|
||||
|
||||
TNode<String> CodeStubAssembler::AllocateConsString(TNode<Uint32T> length,
|
||||
TNode<String> left,
|
||||
TNode<String> right) {
|
||||
// Added string can be a cons string.
|
||||
Comment("Allocating ConsString");
|
||||
TNode<Int32T> left_instance_type = LoadInstanceType(left);
|
||||
TNode<Int32T> right_instance_type = LoadInstanceType(right);
|
||||
|
||||
// Determine the resulting ConsString map to use depending on whether
|
||||
// any of {left} or {right} has two byte encoding.
|
||||
STATIC_ASSERT(kOneByteStringTag != 0);
|
||||
STATIC_ASSERT(kTwoByteStringTag == 0);
|
||||
TNode<Int32T> combined_instance_type =
|
||||
Word32And(left_instance_type, right_instance_type);
|
||||
TNode<Map> result_map = CAST(Select<Object>(
|
||||
IsSetWord32(combined_instance_type, kStringEncodingMask),
|
||||
[=] { return ConsOneByteStringMapConstant(); },
|
||||
[=] { return ConsStringMapConstant(); }));
|
||||
TNode<HeapObject> result = AllocateInNewSpace(ConsString::kSize);
|
||||
StoreMapNoWriteBarrier(result, result_map);
|
||||
StoreObjectFieldNoWriteBarrier(result, ConsString::kLengthOffset, length,
|
||||
MachineRepresentation::kWord32);
|
||||
StoreObjectFieldNoWriteBarrier(result, ConsString::kHashFieldOffset,
|
||||
Int32Constant(String::kEmptyHashField),
|
||||
MachineRepresentation::kWord32);
|
||||
StoreObjectFieldNoWriteBarrier(result, ConsString::kFirstOffset, left);
|
||||
StoreObjectFieldNoWriteBarrier(result, ConsString::kSecondOffset, right);
|
||||
return CAST(result);
|
||||
}
|
||||
|
||||
TNode<NameDictionary> CodeStubAssembler::AllocateNameDictionary(
|
||||
int at_least_space_for) {
|
||||
@ -7395,232 +7366,6 @@ TNode<RawPtrT> ToDirectStringAssembler::TryToSequential(
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
void CodeStubAssembler::BranchIfCanDerefIndirectString(
|
||||
TNode<String> string, TNode<Int32T> instance_type, Label* can_deref,
|
||||
Label* cannot_deref) {
|
||||
TNode<Int32T> representation =
|
||||
Word32And(instance_type, Int32Constant(kStringRepresentationMask));
|
||||
GotoIf(Word32Equal(representation, Int32Constant(kThinStringTag)), can_deref);
|
||||
GotoIf(Word32NotEqual(representation, Int32Constant(kConsStringTag)),
|
||||
cannot_deref);
|
||||
// Cons string.
|
||||
TNode<String> rhs =
|
||||
LoadObjectField<String>(string, ConsString::kSecondOffset);
|
||||
GotoIf(IsEmptyString(rhs), can_deref);
|
||||
Goto(cannot_deref);
|
||||
}
|
||||
|
||||
TNode<String> CodeStubAssembler::DerefIndirectString(
|
||||
TNode<String> string, TNode<Int32T> instance_type, Label* cannot_deref) {
|
||||
Label deref(this);
|
||||
BranchIfCanDerefIndirectString(string, instance_type, &deref, cannot_deref);
|
||||
BIND(&deref);
|
||||
STATIC_ASSERT(static_cast<int>(ThinString::kActualOffset) ==
|
||||
static_cast<int>(ConsString::kFirstOffset));
|
||||
return LoadObjectField<String>(string, ThinString::kActualOffset);
|
||||
}
|
||||
|
||||
void CodeStubAssembler::DerefIndirectString(TVariable<String>* var_string,
|
||||
TNode<Int32T> instance_type) {
|
||||
#ifdef DEBUG
|
||||
Label can_deref(this), cannot_deref(this);
|
||||
BranchIfCanDerefIndirectString(var_string->value(), instance_type, &can_deref,
|
||||
&cannot_deref);
|
||||
BIND(&cannot_deref);
|
||||
DebugBreak(); // Should be able to dereference string.
|
||||
Goto(&can_deref);
|
||||
BIND(&can_deref);
|
||||
#endif // DEBUG
|
||||
|
||||
STATIC_ASSERT(static_cast<int>(ThinString::kActualOffset) ==
|
||||
static_cast<int>(ConsString::kFirstOffset));
|
||||
*var_string =
|
||||
LoadObjectField<String>(var_string->value(), ThinString::kActualOffset);
|
||||
}
|
||||
|
||||
void CodeStubAssembler::MaybeDerefIndirectString(TVariable<String>* var_string,
|
||||
TNode<Int32T> instance_type,
|
||||
Label* did_deref,
|
||||
Label* cannot_deref) {
|
||||
Label deref(this);
|
||||
BranchIfCanDerefIndirectString(var_string->value(), instance_type, &deref,
|
||||
cannot_deref);
|
||||
|
||||
BIND(&deref);
|
||||
{
|
||||
DerefIndirectString(var_string, instance_type);
|
||||
Goto(did_deref);
|
||||
}
|
||||
}
|
||||
|
||||
void CodeStubAssembler::MaybeDerefIndirectStrings(
|
||||
TVariable<String>* var_left, TNode<Int32T> left_instance_type,
|
||||
TVariable<String>* var_right, TNode<Int32T> right_instance_type,
|
||||
Label* did_something) {
|
||||
Label did_nothing_left(this), did_something_left(this),
|
||||
didnt_do_anything(this);
|
||||
MaybeDerefIndirectString(var_left, left_instance_type, &did_something_left,
|
||||
&did_nothing_left);
|
||||
|
||||
BIND(&did_something_left);
|
||||
{
|
||||
MaybeDerefIndirectString(var_right, right_instance_type, did_something,
|
||||
did_something);
|
||||
}
|
||||
|
||||
BIND(&did_nothing_left);
|
||||
{
|
||||
MaybeDerefIndirectString(var_right, right_instance_type, did_something,
|
||||
&didnt_do_anything);
|
||||
}
|
||||
|
||||
BIND(&didnt_do_anything);
|
||||
// Fall through if neither string was an indirect string.
|
||||
}
|
||||
|
||||
TNode<String> CodeStubAssembler::StringAdd(Node* context, TNode<String> left,
|
||||
TNode<String> right) {
|
||||
TVARIABLE(String, result);
|
||||
Label check_right(this), runtime(this, Label::kDeferred), cons(this),
|
||||
done(this, &result), done_native(this, &result);
|
||||
Counters* counters = isolate()->counters();
|
||||
|
||||
TNode<Uint32T> left_length = LoadStringLengthAsWord32(left);
|
||||
GotoIfNot(Word32Equal(left_length, Uint32Constant(0)), &check_right);
|
||||
result = right;
|
||||
Goto(&done_native);
|
||||
|
||||
BIND(&check_right);
|
||||
TNode<Uint32T> right_length = LoadStringLengthAsWord32(right);
|
||||
GotoIfNot(Word32Equal(right_length, Uint32Constant(0)), &cons);
|
||||
result = left;
|
||||
Goto(&done_native);
|
||||
|
||||
BIND(&cons);
|
||||
{
|
||||
TNode<Uint32T> new_length = Uint32Add(left_length, right_length);
|
||||
|
||||
// If new length is greater than String::kMaxLength, goto runtime to
|
||||
// throw. Note: we also need to invalidate the string length protector, so
|
||||
// can't just throw here directly.
|
||||
GotoIf(Uint32GreaterThan(new_length, Uint32Constant(String::kMaxLength)),
|
||||
&runtime);
|
||||
|
||||
TVARIABLE(String, var_left, left);
|
||||
TVARIABLE(String, var_right, right);
|
||||
Variable* input_vars[2] = {&var_left, &var_right};
|
||||
Label non_cons(this, 2, input_vars);
|
||||
Label slow(this, Label::kDeferred);
|
||||
GotoIf(Uint32LessThan(new_length, Uint32Constant(ConsString::kMinLength)),
|
||||
&non_cons);
|
||||
|
||||
result =
|
||||
AllocateConsString(new_length, var_left.value(), var_right.value());
|
||||
Goto(&done_native);
|
||||
|
||||
BIND(&non_cons);
|
||||
|
||||
Comment("Full string concatenate");
|
||||
TNode<Int32T> left_instance_type = LoadInstanceType(var_left.value());
|
||||
TNode<Int32T> right_instance_type = LoadInstanceType(var_right.value());
|
||||
// Compute intersection and difference of instance types.
|
||||
|
||||
TNode<Int32T> ored_instance_types =
|
||||
Word32Or(left_instance_type, right_instance_type);
|
||||
TNode<Word32T> xored_instance_types =
|
||||
Word32Xor(left_instance_type, right_instance_type);
|
||||
|
||||
// Check if both strings have the same encoding and both are sequential.
|
||||
GotoIf(IsSetWord32(xored_instance_types, kStringEncodingMask), &runtime);
|
||||
GotoIf(IsSetWord32(ored_instance_types, kStringRepresentationMask), &slow);
|
||||
|
||||
TNode<IntPtrT> word_left_length = Signed(ChangeUint32ToWord(left_length));
|
||||
TNode<IntPtrT> word_right_length = Signed(ChangeUint32ToWord(right_length));
|
||||
|
||||
Label two_byte(this);
|
||||
GotoIf(Word32Equal(Word32And(ored_instance_types,
|
||||
Int32Constant(kStringEncodingMask)),
|
||||
Int32Constant(kTwoByteStringTag)),
|
||||
&two_byte);
|
||||
// One-byte sequential string case
|
||||
result = AllocateSeqOneByteString(new_length);
|
||||
CopyStringCharacters(var_left.value(), result.value(), IntPtrConstant(0),
|
||||
IntPtrConstant(0), word_left_length,
|
||||
String::ONE_BYTE_ENCODING, String::ONE_BYTE_ENCODING);
|
||||
CopyStringCharacters(var_right.value(), result.value(), IntPtrConstant(0),
|
||||
word_left_length, word_right_length,
|
||||
String::ONE_BYTE_ENCODING, String::ONE_BYTE_ENCODING);
|
||||
Goto(&done_native);
|
||||
|
||||
BIND(&two_byte);
|
||||
{
|
||||
// Two-byte sequential string case
|
||||
result = AllocateSeqTwoByteString(new_length);
|
||||
CopyStringCharacters(var_left.value(), result.value(), IntPtrConstant(0),
|
||||
IntPtrConstant(0), word_left_length,
|
||||
String::TWO_BYTE_ENCODING,
|
||||
String::TWO_BYTE_ENCODING);
|
||||
CopyStringCharacters(var_right.value(), result.value(), IntPtrConstant(0),
|
||||
word_left_length, word_right_length,
|
||||
String::TWO_BYTE_ENCODING,
|
||||
String::TWO_BYTE_ENCODING);
|
||||
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 = CAST(CallRuntime(Runtime::kStringAdd, context, left, right));
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&done_native);
|
||||
{
|
||||
IncrementCounter(counters->string_add_native(), 1);
|
||||
Goto(&done);
|
||||
}
|
||||
|
||||
BIND(&done);
|
||||
return result.value();
|
||||
}
|
||||
|
||||
TNode<String> CodeStubAssembler::StringFromSingleUTF16EncodedCodePoint(
|
||||
TNode<Int32T> codepoint) {
|
||||
VARIABLE(var_result, MachineRepresentation::kTagged, 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(StringFromSingleCharCode(codepoint));
|
||||
Goto(&return_result);
|
||||
}
|
||||
|
||||
BIND(&if_isword32);
|
||||
{
|
||||
TNode<String> value = AllocateSeqTwoByteString(2);
|
||||
StoreNoWriteBarrier(
|
||||
MachineRepresentation::kWord32, value,
|
||||
IntPtrConstant(SeqTwoByteString::kHeaderSize - kHeapObjectTag),
|
||||
codepoint);
|
||||
var_result.Bind(value);
|
||||
Goto(&return_result);
|
||||
}
|
||||
|
||||
BIND(&return_result);
|
||||
return CAST(var_result.value());
|
||||
}
|
||||
|
||||
TNode<Number> CodeStubAssembler::StringToNumber(TNode<String> input) {
|
||||
Label runtime(this, Label::kDeferred);
|
||||
Label end(this);
|
||||
|
@ -1739,11 +1739,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
TNode<String> parent,
|
||||
TNode<Smi> offset);
|
||||
|
||||
// Allocate an appropriate one- or two-byte ConsString with the first and
|
||||
// second parts specified by |left| and |right|.
|
||||
TNode<String> AllocateConsString(TNode<Uint32T> length, TNode<String> left,
|
||||
TNode<String> right);
|
||||
|
||||
TNode<NameDictionary> AllocateNameDictionary(int at_least_space_for);
|
||||
TNode<NameDictionary> AllocateNameDictionary(
|
||||
TNode<IntPtrT> at_least_space_for, AllocationFlags = kNone);
|
||||
@ -2617,36 +2612,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
||||
TNode<String> SubString(TNode<String> string, TNode<IntPtrT> from,
|
||||
TNode<IntPtrT> to);
|
||||
|
||||
// Return a new string object produced by concatenating |first| with |second|.
|
||||
TNode<String> StringAdd(Node* context, TNode<String> first,
|
||||
TNode<String> second);
|
||||
|
||||
// Check if |string| is an indirect (thin or flat cons) string type that can
|
||||
// be dereferenced by DerefIndirectString.
|
||||
void BranchIfCanDerefIndirectString(TNode<String> string,
|
||||
TNode<Int32T> instance_type,
|
||||
Label* can_deref, Label* cannot_deref);
|
||||
// Unpack an indirect (thin or flat cons) string type.
|
||||
void DerefIndirectString(TVariable<String>* var_string,
|
||||
TNode<Int32T> instance_type);
|
||||
// Check if |var_string| has an indirect (thin or flat cons) string type,
|
||||
// and unpack it if so.
|
||||
void MaybeDerefIndirectString(TVariable<String>* var_string,
|
||||
TNode<Int32T> instance_type, Label* did_deref,
|
||||
Label* cannot_deref);
|
||||
// Check if |var_left| or |var_right| has an indirect (thin or flat cons)
|
||||
// string type, and unpack it/them if so. Fall through if nothing was done.
|
||||
void MaybeDerefIndirectStrings(TVariable<String>* var_left,
|
||||
TNode<Int32T> left_instance_type,
|
||||
TVariable<String>* var_right,
|
||||
TNode<Int32T> right_instance_type,
|
||||
Label* did_something);
|
||||
TNode<String> DerefIndirectString(TNode<String> string,
|
||||
TNode<Int32T> instance_type,
|
||||
Label* cannot_deref);
|
||||
|
||||
TNode<String> StringFromSingleUTF16EncodedCodePoint(TNode<Int32T> codepoint);
|
||||
|
||||
// Type conversion helpers.
|
||||
enum class BigIntHandling { kConvertToNumber, kThrow };
|
||||
// Convert a String to a Number.
|
||||
|
Loading…
Reference in New Issue
Block a user