[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:
Dan Elphick 2019-09-23 15:45:34 +01:00 committed by Commit Bot
parent 3616229f12
commit 33ff3b05a3
5 changed files with 293 additions and 291 deletions

View File

@ -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));

View File

@ -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:
//

View File

@ -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):

View File

@ -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);

View File

@ -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.