Commit 9e2fd36c authored by danno's avatar danno Committed by Commit bot

[stubs]: Support 1->2 byte copies in CopyStringCharacters

In the process, add a more general mechanism for passing around
and amending list of CodeStubAssembler Variables. That change
makes it possible to more easily add Variables to loops that are
generated by utility functions, e.g. BuildFastLoop.

LOG=N

Review-Url: https://codereview.chromium.org/2461363002
Cr-Commit-Position: refs/heads/master@{#40700}
parent fe552636
......@@ -2073,47 +2073,66 @@ void CodeStubAssembler::CopyFixedArrayElements(
Comment("] CopyFixedArrayElements");
}
void CodeStubAssembler::CopyStringCharacters(compiler::Node* from_string,
compiler::Node* to_string,
compiler::Node* from_index,
compiler::Node* to_index,
compiler::Node* character_count,
String::Encoding encoding,
ParameterMode mode) {
bool one_byte = encoding == String::ONE_BYTE_ENCODING;
Comment(one_byte ? "CopyStringCharacters ONE_BYTE_ENCODING"
: "CopyStringCharacters TWO_BYTE_ENCODING");
ElementsKind kind = one_byte ? UINT8_ELEMENTS : UINT16_ELEMENTS;
int header_size = (one_byte ? SeqOneByteString::kHeaderSize
: SeqTwoByteString::kHeaderSize) -
kHeapObjectTag;
Node* from_offset = ElementOffsetFromIndex(from_index, kind, mode);
Node* to_offset = ElementOffsetFromIndex(to_index, kind, mode);
Node* byte_count = ElementOffsetFromIndex(character_count, kind, mode);
void CodeStubAssembler::CopyStringCharacters(
compiler::Node* from_string, compiler::Node* to_string,
compiler::Node* from_index, compiler::Node* to_index,
compiler::Node* character_count, String::Encoding from_encoding,
String::Encoding to_encoding, ParameterMode mode) {
bool from_one_byte = from_encoding == String::ONE_BYTE_ENCODING;
bool to_one_byte = to_encoding == String::ONE_BYTE_ENCODING;
DCHECK_IMPLIES(to_one_byte, from_one_byte);
Comment("CopyStringCharacters %s -> %s",
from_one_byte ? "ONE_BYTE_ENCODING" : "TWO_BYTE_ENCODING",
to_one_byte ? "ONE_BYTE_ENCODING" : "TWO_BYTE_ENCODING");
ElementsKind from_kind = from_one_byte ? UINT8_ELEMENTS : UINT16_ELEMENTS;
ElementsKind to_kind = to_one_byte ? UINT8_ELEMENTS : UINT16_ELEMENTS;
STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
int header_size = SeqOneByteString::kHeaderSize - kHeapObjectTag;
Node* from_offset =
ElementOffsetFromIndex(from_index, from_kind, mode, header_size);
Node* to_offset =
ElementOffsetFromIndex(to_index, to_kind, mode, header_size);
Node* byte_count = ElementOffsetFromIndex(character_count, from_kind, mode);
Node* limit_offset = IntPtrAddFoldConstants(from_offset, byte_count);
// Prepare the fast loop
MachineType type = one_byte ? MachineType::Uint8() : MachineType::Uint16();
MachineRepresentation rep =
one_byte ? MachineRepresentation::kWord8 : MachineRepresentation::kWord16;
int increment = -(1 << ElementsKindToShiftSize(kind));
Node* to_string_adjusted = IntPtrAddFoldConstants(
to_string, IntPtrSubFoldConstants(to_offset, from_offset));
limit_offset =
IntPtrAddFoldConstants(limit_offset, IntPtrConstant(header_size));
from_offset =
IntPtrAddFoldConstants(from_offset, IntPtrConstant(header_size));
BuildFastLoop(MachineType::PointerRepresentation(), limit_offset, from_offset,
[from_string, to_string_adjusted, type, rep](
CodeStubAssembler* assembler, Node* offset) {
MachineType type =
from_one_byte ? MachineType::Uint8() : MachineType::Uint16();
MachineRepresentation rep = to_one_byte ? MachineRepresentation::kWord8
: MachineRepresentation::kWord16;
int from_increment = 1 << ElementsKindToShiftSize(from_kind);
int to_increment = 1 << ElementsKindToShiftSize(to_kind);
Variable current_to_offset(this, MachineType::PointerRepresentation());
VariableList vars({&current_to_offset}, zone());
current_to_offset.Bind(to_offset);
int to_index_constant = 0, from_index_constant = 0;
Smi* to_index_smi = nullptr;
Smi* from_index_smi = nullptr;
bool index_same = (from_encoding == to_encoding) &&
(from_index == to_index ||
(ToInt32Constant(from_index, from_index_constant) &&
ToInt32Constant(to_index, to_index_constant) &&
from_index_constant == to_index_constant) ||
(ToSmiConstant(from_index, from_index_smi) &&
ToSmiConstant(to_index, to_index_smi) &&
to_index_smi == from_index_smi));
BuildFastLoop(vars, MachineType::PointerRepresentation(), from_offset,
limit_offset,
[from_string, to_string, &current_to_offset, to_increment, type,
rep, index_same](CodeStubAssembler* assembler, Node* offset) {
Node* value = assembler->Load(type, from_string, offset);
assembler->StoreNoWriteBarrier(rep, to_string_adjusted,
offset, value);
assembler->StoreNoWriteBarrier(
rep, to_string,
index_same ? offset : current_to_offset.value(), value);
if (!index_same) {
current_to_offset.Bind(assembler->IntPtrAdd(
current_to_offset.value(),
assembler->IntPtrConstant(to_increment)));
}
},
increment);
from_increment, IndexAdvanceMode::kPost);
}
Node* CodeStubAssembler::LoadElementAndPrepareForStore(Node* array,
......@@ -2982,6 +3001,7 @@ Node* AllocAndCopyStringCharacters(CodeStubAssembler* a, Node* context,
Node* result =
a->AllocateSeqOneByteString(context, a->SmiToWord(character_count));
a->CopyStringCharacters(from, result, from_index, smi_zero, character_count,
String::ONE_BYTE_ENCODING,
String::ONE_BYTE_ENCODING,
CodeStubAssembler::SMI_PARAMETERS);
var_result.Bind(result);
......@@ -2995,6 +3015,7 @@ Node* AllocAndCopyStringCharacters(CodeStubAssembler* a, Node* context,
Node* result =
a->AllocateSeqTwoByteString(context, a->SmiToWord(character_count));
a->CopyStringCharacters(from, result, from_index, smi_zero, character_count,
String::TWO_BYTE_ENCODING,
String::TWO_BYTE_ENCODING,
CodeStubAssembler::SMI_PARAMETERS);
var_result.Bind(result);
......@@ -3297,9 +3318,11 @@ Node* CodeStubAssembler::StringAdd(Node* context, Node* left, Node* right,
AllocateSeqOneByteString(context, new_length, SMI_PARAMETERS);
CopyStringCharacters(left, new_string, SmiConstant(Smi::kZero),
SmiConstant(Smi::kZero), left_length,
String::ONE_BYTE_ENCODING, SMI_PARAMETERS);
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, SMI_PARAMETERS);
right_length, String::ONE_BYTE_ENCODING,
String::ONE_BYTE_ENCODING, SMI_PARAMETERS);
result.Bind(new_string);
Goto(&done_native);
......@@ -3309,10 +3332,11 @@ Node* CodeStubAssembler::StringAdd(Node* context, Node* left, Node* right,
new_string = AllocateSeqTwoByteString(context, new_length, SMI_PARAMETERS);
CopyStringCharacters(left, new_string, SmiConstant(Smi::kZero),
SmiConstant(Smi::kZero), left_length,
String::TWO_BYTE_ENCODING, SMI_PARAMETERS);
String::TWO_BYTE_ENCODING, String::TWO_BYTE_ENCODING,
SMI_PARAMETERS);
CopyStringCharacters(right, new_string, SmiConstant(Smi::kZero),
left_length, right_length, String::TWO_BYTE_ENCODING,
SMI_PARAMETERS);
String::TWO_BYTE_ENCODING, SMI_PARAMETERS);
result.Bind(new_string);
Goto(&done_native);
}
......@@ -6964,12 +6988,15 @@ Node* CodeStubAssembler::CreateWeakCellInFeedbackVector(Node* feedback_vector,
}
void CodeStubAssembler::BuildFastLoop(
const CodeStubAssembler::VariableList& vars,
MachineRepresentation index_rep, Node* start_index, Node* end_index,
std::function<void(CodeStubAssembler* assembler, Node* index)> body,
int increment, IndexAdvanceMode mode) {
Variable var(this, index_rep);
VariableList vars_copy(vars, zone());
vars_copy.Add(&var, zone());
var.Bind(start_index);
Label loop(this, &var);
Label loop(this, vars_copy);
Label after_loop(this);
// Introduce an explicit second check of the termination condition before the
// loop that helps turbofan generate better code. If there's only a single
......
......@@ -518,7 +518,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// Copies |character_count| elements from |from_string| to |to_string|
// starting at the |from_index|'th character. |from_string| and |to_string|
// must be either both one-byte strings or both two-byte strings.
// can either be one-byte strings or two-byte strings, although if
// |from_string| is two-byte, then |to_string| must be two-byte.
// |from_index|, |to_index| and |character_count| must be either Smis or
// intptr_ts depending on |mode| s.t. 0 <= |from_index| <= |from_index| +
// |character_count| <= from_string.length and 0 <= |to_index| <= |to_index| +
......@@ -528,7 +529,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
compiler::Node* from_index,
compiler::Node* to_index,
compiler::Node* character_count,
String::Encoding encoding, ParameterMode mode);
String::Encoding from_encoding,
String::Encoding to_encoding, ParameterMode mode);
// Loads an element from |array| of |from_kind| elements by given |offset|
// (NOTE: not index!), does a hole check if |if_hole| is provided and
......@@ -1022,12 +1024,22 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
enum class IndexAdvanceMode { kPre, kPost };
void BuildFastLoop(
const VariableList& var_list, MachineRepresentation index_rep,
compiler::Node* start_index, compiler::Node* end_index,
std::function<void(CodeStubAssembler* assembler, compiler::Node* index)>
body,
int increment, IndexAdvanceMode mode = IndexAdvanceMode::kPre);
void BuildFastLoop(
MachineRepresentation index_rep, compiler::Node* start_index,
compiler::Node* end_index,
std::function<void(CodeStubAssembler* assembler, compiler::Node* index)>
body,
int increment, IndexAdvanceMode mode = IndexAdvanceMode::kPre);
int increment, IndexAdvanceMode mode = IndexAdvanceMode::kPre) {
BuildFastLoop(VariableList(0, zone()), index_rep, start_index, end_index,
body, increment, mode);
}
enum class ForEachDirection { kForward, kReverse };
......
......@@ -1066,16 +1066,15 @@ bool CodeAssembler::Variable::IsBound() const {
return impl_->value_ != nullptr;
}
CodeAssembler::Label::Label(CodeAssembler* assembler, int merged_value_count,
CodeAssembler::Variable** merged_variables,
CodeAssembler::Label::Type type)
CodeAssembler::Label::Label(CodeAssembler* assembler, size_t vars_count,
Variable** vars, CodeAssembler::Label::Type type)
: bound_(false), merge_count_(0), assembler_(assembler), label_(nullptr) {
void* buffer = assembler->zone()->New(sizeof(RawMachineLabel));
label_ = new (buffer)
RawMachineLabel(type == kDeferred ? RawMachineLabel::kDeferred
: RawMachineLabel::kNonDeferred);
for (int i = 0; i < merged_value_count; ++i) {
variable_phis_[merged_variables[i]->impl_] = nullptr;
for (size_t i = 0; i < vars_count; ++i) {
variable_phis_[vars[i]->impl_] = nullptr;
}
}
......
......@@ -212,6 +212,8 @@ class V8_EXPORT_PRIVATE CodeAssembler {
CodeAssembler* assembler_;
};
typedef ZoneList<Variable*> VariableList;
// ===========================================================================
// Base Assembler
// ===========================================================================
......@@ -492,12 +494,15 @@ class CodeAssembler::Label {
CodeAssembler* assembler,
CodeAssembler::Label::Type type = CodeAssembler::Label::kNonDeferred)
: CodeAssembler::Label(assembler, 0, nullptr, type) {}
Label(CodeAssembler* assembler, CodeAssembler::Variable* merged_variable,
Label(CodeAssembler* assembler, const VariableList& merged_variables,
CodeAssembler::Label::Type type = CodeAssembler::Label::kNonDeferred)
: CodeAssembler::Label(assembler, 1, &merged_variable, type) {}
Label(CodeAssembler* assembler, int merged_variable_count,
CodeAssembler::Variable** merged_variables,
: CodeAssembler::Label(assembler, merged_variables.length(),
&(merged_variables[0]), type) {}
Label(CodeAssembler* assembler, size_t count, Variable** vars,
CodeAssembler::Label::Type type = CodeAssembler::Label::kNonDeferred);
Label(CodeAssembler* assembler, CodeAssembler::Variable* merged_variable,
CodeAssembler::Label::Type type = CodeAssembler::Label::kNonDeferred)
: Label(assembler, 1, &merged_variable, type) {}
~Label() {}
private:
......
......@@ -160,6 +160,13 @@ class ZoneList final : public List<T, ZoneAllocationPolicy> {
ZoneList(int capacity, Zone* zone)
: List<T, ZoneAllocationPolicy>(capacity, ZoneAllocationPolicy(zone)) {}
// Construct a new ZoneList from a std::initializer_list
ZoneList(std::initializer_list<T> list, Zone* zone)
: List<T, ZoneAllocationPolicy>(static_cast<int>(list.size()),
ZoneAllocationPolicy(zone)) {
for (auto& i : list) Add(i, zone);
}
void* operator new(size_t size, Zone* zone) { return zone->New(size); }
// Construct a new ZoneList by copying the elements of the given ZoneList.
......
......@@ -1785,5 +1785,141 @@ TEST(PopAndReturnVariable) {
}
}
TEST(OneToTwoByteStringCopy) {
Isolate* isolate(CcTest::InitIsolateOnce());
CodeStubAssemblerTester m(isolate, 2);
m.CopyStringCharacters(
m.Parameter(0), m.Parameter(1), m.SmiConstant(Smi::FromInt(0)),
m.SmiConstant(Smi::FromInt(0)), m.SmiConstant(Smi::FromInt(5)),
String::ONE_BYTE_ENCODING, String::TWO_BYTE_ENCODING,
CodeStubAssembler::SMI_PARAMETERS);
m.Return(m.SmiConstant(Smi::FromInt(0)));
Handle<Code> code = m.GenerateCode();
CHECK(!code.is_null());
Handle<String> string1 = isolate->factory()->InternalizeUtf8String("abcde");
uc16 array[] = {1000, 1001, 1002, 1003, 1004};
Vector<const uc16> str(array);
Handle<String> string2 =
isolate->factory()->NewStringFromTwoByte(str).ToHandleChecked();
FunctionTester ft(code, 2);
ft.Call(string1, string2);
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars()[0],
Handle<SeqTwoByteString>::cast(string2)->GetChars()[0]);
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars()[1],
Handle<SeqTwoByteString>::cast(string2)->GetChars()[1]);
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars()[2],
Handle<SeqTwoByteString>::cast(string2)->GetChars()[2]);
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars()[3],
Handle<SeqTwoByteString>::cast(string2)->GetChars()[3]);
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars()[4],
Handle<SeqTwoByteString>::cast(string2)->GetChars()[4]);
}
TEST(OneToOneByteStringCopy) {
Isolate* isolate(CcTest::InitIsolateOnce());
CodeStubAssemblerTester m(isolate, 2);
m.CopyStringCharacters(
m.Parameter(0), m.Parameter(1), m.SmiConstant(Smi::FromInt(0)),
m.SmiConstant(Smi::FromInt(0)), m.SmiConstant(Smi::FromInt(5)),
String::ONE_BYTE_ENCODING, String::ONE_BYTE_ENCODING,
CodeStubAssembler::SMI_PARAMETERS);
m.Return(m.SmiConstant(Smi::FromInt(0)));
Handle<Code> code = m.GenerateCode();
CHECK(!code.is_null());
Handle<String> string1 = isolate->factory()->InternalizeUtf8String("abcde");
uint8_t array[] = {100, 101, 102, 103, 104};
Vector<const uint8_t> str(array);
Handle<String> string2 =
isolate->factory()->NewStringFromOneByte(str).ToHandleChecked();
FunctionTester ft(code, 2);
ft.Call(string1, string2);
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars()[0],
Handle<SeqOneByteString>::cast(string2)->GetChars()[0]);
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars()[1],
Handle<SeqOneByteString>::cast(string2)->GetChars()[1]);
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars()[2],
Handle<SeqOneByteString>::cast(string2)->GetChars()[2]);
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars()[3],
Handle<SeqOneByteString>::cast(string2)->GetChars()[3]);
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars()[4],
Handle<SeqOneByteString>::cast(string2)->GetChars()[4]);
}
TEST(OneToOneByteStringCopyNonZeroStart) {
Isolate* isolate(CcTest::InitIsolateOnce());
CodeStubAssemblerTester m(isolate, 2);
m.CopyStringCharacters(
m.Parameter(0), m.Parameter(1), m.SmiConstant(Smi::FromInt(0)),
m.SmiConstant(Smi::FromInt(3)), m.SmiConstant(Smi::FromInt(2)),
String::ONE_BYTE_ENCODING, String::ONE_BYTE_ENCODING,
CodeStubAssembler::SMI_PARAMETERS);
m.Return(m.SmiConstant(Smi::FromInt(0)));
Handle<Code> code = m.GenerateCode();
CHECK(!code.is_null());
Handle<String> string1 = isolate->factory()->InternalizeUtf8String("abcde");
uint8_t array[] = {100, 101, 102, 103, 104};
Vector<const uint8_t> str(array);
Handle<String> string2 =
isolate->factory()->NewStringFromOneByte(str).ToHandleChecked();
FunctionTester ft(code, 2);
ft.Call(string1, string2);
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars()[0],
Handle<SeqOneByteString>::cast(string2)->GetChars()[3]);
CHECK_EQ(Handle<SeqOneByteString>::cast(string1)->GetChars()[1],
Handle<SeqOneByteString>::cast(string2)->GetChars()[4]);
CHECK_EQ(100, Handle<SeqOneByteString>::cast(string2)->GetChars()[0]);
CHECK_EQ(101, Handle<SeqOneByteString>::cast(string2)->GetChars()[1]);
CHECK_EQ(102, Handle<SeqOneByteString>::cast(string2)->GetChars()[2]);
}
TEST(TwoToTwoByteStringCopy) {
Isolate* isolate(CcTest::InitIsolateOnce());
CodeStubAssemblerTester m(isolate, 2);
m.CopyStringCharacters(
m.Parameter(0), m.Parameter(1), m.SmiConstant(Smi::FromInt(0)),
m.SmiConstant(Smi::FromInt(0)), m.SmiConstant(Smi::FromInt(5)),
String::TWO_BYTE_ENCODING, String::TWO_BYTE_ENCODING,
CodeStubAssembler::SMI_PARAMETERS);
m.Return(m.SmiConstant(Smi::FromInt(0)));
Handle<Code> code = m.GenerateCode();
CHECK(!code.is_null());
uc16 array1[] = {2000, 2001, 2002, 2003, 2004};
Vector<const uc16> str1(array1);
Handle<String> string1 =
isolate->factory()->NewStringFromTwoByte(str1).ToHandleChecked();
uc16 array2[] = {1000, 1001, 1002, 1003, 1004};
Vector<const uc16> str2(array2);
Handle<String> string2 =
isolate->factory()->NewStringFromTwoByte(str2).ToHandleChecked();
FunctionTester ft(code, 2);
ft.Call(string1, string2);
CHECK_EQ(Handle<SeqTwoByteString>::cast(string1)->GetChars()[0],
Handle<SeqTwoByteString>::cast(string2)->GetChars()[0]);
CHECK_EQ(Handle<SeqTwoByteString>::cast(string1)->GetChars()[1],
Handle<SeqTwoByteString>::cast(string2)->GetChars()[1]);
CHECK_EQ(Handle<SeqTwoByteString>::cast(string1)->GetChars()[2],
Handle<SeqTwoByteString>::cast(string2)->GetChars()[2]);
CHECK_EQ(Handle<SeqTwoByteString>::cast(string1)->GetChars()[3],
Handle<SeqTwoByteString>::cast(string2)->GetChars()[3]);
CHECK_EQ(Handle<SeqTwoByteString>::cast(string1)->GetChars()[4],
Handle<SeqTwoByteString>::cast(string2)->GetChars()[4]);
}
} // namespace internal
} // namespace v8
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