Commit 0f94e0d3 authored by machenbach's avatar machenbach Committed by Commit bot

Revert of [fullcodegen] Remove the hacky %_FastOneByteArrayJoin intrinsic....

Revert of [fullcodegen] Remove the hacky %_FastOneByteArrayJoin intrinsic. (patchset #1 id:1 of https://codereview.chromium.org/1708523002/ )

Reason for revert:
[Sheriff] Speculative revert for adding arm flakes:
https://build.chromium.org/p/client.v8/builders/V8%20Linux%20-%20arm%20-%20sim/builds/5756
https://build.chromium.org/p/client.v8/builders/V8%20Linux%20-%20arm%20-%20sim%20-%20novfp3/builds/3375/

And some more...

Original issue's description:
> [fullcodegen] Remove the hacky %_FastOneByteArrayJoin intrinsic.
>
> This intrinsic was only supported in fullcodegen, and is actually no
> longer relevant for SunSpider peak performance it seems, so let's get
> rid of it and maybe just implement Array.prototype.join with a fast
> path at some point instead.
>
> R=mstarzinger@chromium.org
>
> Committed: https://crrev.com/ccf12b4bede3f1ce3ce14fb33bcc4041525a40af
> Cr-Commit-Position: refs/heads/master@{#34084}

TBR=mstarzinger@chromium.org,bmeurer@chromium.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true

Review URL: https://codereview.chromium.org/1709693002

Cr-Commit-Position: refs/heads/master@{#34091}
parent 789ad2f4
......@@ -97,6 +97,8 @@ namespace internal {
V(kImportDeclaration, "Import declaration") \
V(kIndexIsNegative, "Index is negative") \
V(kIndexIsTooLarge, "Index is too large") \
V(kInlinedRuntimeFunctionFastOneByteArrayJoin, \
"Inlined runtime function: FastOneByteArrayJoin") \
V(kInliningBailedOut, "Inlining bailed out") \
V(kInputGPRIsExpectedToHaveUpper32Cleared, \
"Input GPR is expected to have upper32 cleared") \
......@@ -127,6 +129,8 @@ namespace internal {
V(kNativeFunctionLiteral, "Native function literal") \
V(kNeedSmiLiteral, "Need a Smi literal here") \
V(kNoCasesLeft, "No cases left") \
V(kNoEmptyArraysHereInEmitFastOneByteArrayJoin, \
"No empty arrays here in EmitFastOneByteArrayJoin") \
V(kNonInitializerAssignmentToConst, "Non-initializer assignment to const") \
V(kNonSmiIndex, "Non-smi index") \
V(kNonSmiKeyInArrayLiteral, "Non-smi key in array literal") \
......
......@@ -12900,6 +12900,16 @@ void HOptimizedGraphBuilder::GenerateGetCachedArrayIndex(CallRuntime* call) {
}
void HOptimizedGraphBuilder::GenerateFastOneByteArrayJoin(CallRuntime* call) {
// Simply returning undefined here would be semantically correct and even
// avoid the bailout. Nevertheless, some ancient benchmarks like SunSpider's
// string-fasta would tank, because fullcode contains an optimized version.
// Obviously the fullcode => Crankshaft => bailout => fullcode dance is
// faster... *sigh*
return Bailout(kInlinedRuntimeFunctionFastOneByteArrayJoin);
}
void HOptimizedGraphBuilder::GenerateDebugBreakInOptimizedCode(
CallRuntime* call) {
Add<HDebugBreak>();
......
......@@ -2213,6 +2213,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
F(MathPow) \
F(HasCachedArrayIndex) \
F(GetCachedArrayIndex) \
F(FastOneByteArrayJoin) \
F(DebugBreakInOptimizedCode) \
F(StringCharCodeAt) \
F(SubString) \
......
......@@ -3414,6 +3414,243 @@ void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) {
}
void FullCodeGenerator::EmitFastOneByteArrayJoin(CallRuntime* expr) {
Label bailout, done, one_char_separator, long_separator, non_trivial_array,
not_size_one_array, loop, empty_separator_loop, one_char_separator_loop,
one_char_separator_loop_entry, long_separator_loop;
ZoneList<Expression*>* args = expr->arguments();
DCHECK(args->length() == 2);
VisitForStackValue(args->at(1));
VisitForAccumulatorValue(args->at(0));
// All aliases of the same register have disjoint lifetimes.
Register array = r0;
Register elements = no_reg; // Will be r0.
Register result = no_reg; // Will be r0.
Register separator = r1;
Register array_length = r2;
Register result_pos = no_reg; // Will be r2
Register string_length = r3;
Register string = r4;
Register element = r5;
Register elements_end = r6;
Register scratch = r9;
// Separator operand is on the stack.
__ pop(separator);
// Check that the array is a JSArray.
__ JumpIfSmi(array, &bailout);
__ CompareObjectType(array, scratch, array_length, JS_ARRAY_TYPE);
__ b(ne, &bailout);
// Check that the array has fast elements.
__ CheckFastElements(scratch, array_length, &bailout);
// If the array has length zero, return the empty string.
__ ldr(array_length, FieldMemOperand(array, JSArray::kLengthOffset));
__ SmiUntag(array_length, SetCC);
__ b(ne, &non_trivial_array);
__ LoadRoot(r0, Heap::kempty_stringRootIndex);
__ b(&done);
__ bind(&non_trivial_array);
// Get the FixedArray containing array's elements.
elements = array;
__ ldr(elements, FieldMemOperand(array, JSArray::kElementsOffset));
array = no_reg; // End of array's live range.
// Check that all array elements are sequential one-byte strings, and
// accumulate the sum of their lengths, as a smi-encoded value.
__ mov(string_length, Operand::Zero());
__ add(element,
elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
__ add(elements_end, element, Operand(array_length, LSL, kPointerSizeLog2));
// Loop condition: while (element < elements_end).
// Live values in registers:
// elements: Fixed array of strings.
// array_length: Length of the fixed array of strings (not smi)
// separator: Separator string
// string_length: Accumulated sum of string lengths (smi).
// element: Current array element.
// elements_end: Array end.
if (generate_debug_code_) {
__ cmp(array_length, Operand::Zero());
__ Assert(gt, kNoEmptyArraysHereInEmitFastOneByteArrayJoin);
}
__ bind(&loop);
__ ldr(string, MemOperand(element, kPointerSize, PostIndex));
__ JumpIfSmi(string, &bailout);
__ ldr(scratch, FieldMemOperand(string, HeapObject::kMapOffset));
__ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
__ JumpIfInstanceTypeIsNotSequentialOneByte(scratch, scratch, &bailout);
__ ldr(scratch, FieldMemOperand(string, SeqOneByteString::kLengthOffset));
__ add(string_length, string_length, Operand(scratch), SetCC);
__ b(vs, &bailout);
__ cmp(element, elements_end);
__ b(lt, &loop);
// If array_length is 1, return elements[0], a string.
__ cmp(array_length, Operand(1));
__ b(ne, &not_size_one_array);
__ ldr(r0, FieldMemOperand(elements, FixedArray::kHeaderSize));
__ b(&done);
__ bind(&not_size_one_array);
// Live values in registers:
// separator: Separator string
// array_length: Length of the array.
// string_length: Sum of string lengths (smi).
// elements: FixedArray of strings.
// Check that the separator is a flat one-byte string.
__ JumpIfSmi(separator, &bailout);
__ ldr(scratch, FieldMemOperand(separator, HeapObject::kMapOffset));
__ ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
__ JumpIfInstanceTypeIsNotSequentialOneByte(scratch, scratch, &bailout);
// Add (separator length times array_length) - separator length to the
// string_length to get the length of the result string. array_length is not
// smi but the other values are, so the result is a smi
__ ldr(scratch, FieldMemOperand(separator, SeqOneByteString::kLengthOffset));
__ sub(string_length, string_length, Operand(scratch));
__ smull(scratch, ip, array_length, scratch);
// Check for smi overflow. No overflow if higher 33 bits of 64-bit result are
// zero.
__ cmp(ip, Operand::Zero());
__ b(ne, &bailout);
__ tst(scratch, Operand(0x80000000));
__ b(ne, &bailout);
__ add(string_length, string_length, Operand(scratch), SetCC);
__ b(vs, &bailout);
__ SmiUntag(string_length);
// Bailout for large object allocations.
__ cmp(string_length, Operand(Page::kMaxRegularHeapObjectSize));
__ b(gt, &bailout);
// Get first element in the array to free up the elements register to be used
// for the result.
__ add(element,
elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
result = elements; // End of live range for elements.
elements = no_reg;
// Live values in registers:
// element: First array element
// separator: Separator string
// string_length: Length of result string (not smi)
// array_length: Length of the array.
__ AllocateOneByteString(result, string_length, scratch,
string, // used as scratch
elements_end, // used as scratch
&bailout);
// Prepare for looping. Set up elements_end to end of the array. Set
// result_pos to the position of the result where to write the first
// character.
__ add(elements_end, element, Operand(array_length, LSL, kPointerSizeLog2));
result_pos = array_length; // End of live range for array_length.
array_length = no_reg;
__ add(result_pos,
result,
Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
// Check the length of the separator.
__ ldr(scratch, FieldMemOperand(separator, SeqOneByteString::kLengthOffset));
__ cmp(scratch, Operand(Smi::FromInt(1)));
__ b(eq, &one_char_separator);
__ b(gt, &long_separator);
// Empty separator case
__ bind(&empty_separator_loop);
// Live values in registers:
// result_pos: the position to which we are currently copying characters.
// element: Current array element.
// elements_end: Array end.
// Copy next array element to the result.
__ ldr(string, MemOperand(element, kPointerSize, PostIndex));
__ ldr(string_length, FieldMemOperand(string, String::kLengthOffset));
__ SmiUntag(string_length);
__ add(string,
string,
Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
__ CopyBytes(string, result_pos, string_length, scratch);
__ cmp(element, elements_end);
__ b(lt, &empty_separator_loop); // End while (element < elements_end).
DCHECK(result.is(r0));
__ b(&done);
// One-character separator case
__ bind(&one_char_separator);
// Replace separator with its one-byte character value.
__ ldrb(separator, FieldMemOperand(separator, SeqOneByteString::kHeaderSize));
// Jump into the loop after the code that copies the separator, so the first
// element is not preceded by a separator
__ jmp(&one_char_separator_loop_entry);
__ bind(&one_char_separator_loop);
// Live values in registers:
// result_pos: the position to which we are currently copying characters.
// element: Current array element.
// elements_end: Array end.
// separator: Single separator one-byte char (in lower byte).
// Copy the separator character to the result.
__ strb(separator, MemOperand(result_pos, 1, PostIndex));
// Copy next array element to the result.
__ bind(&one_char_separator_loop_entry);
__ ldr(string, MemOperand(element, kPointerSize, PostIndex));
__ ldr(string_length, FieldMemOperand(string, String::kLengthOffset));
__ SmiUntag(string_length);
__ add(string,
string,
Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
__ CopyBytes(string, result_pos, string_length, scratch);
__ cmp(element, elements_end);
__ b(lt, &one_char_separator_loop); // End while (element < elements_end).
DCHECK(result.is(r0));
__ b(&done);
// Long separator case (separator is more than one character). Entry is at the
// label long_separator below.
__ bind(&long_separator_loop);
// Live values in registers:
// result_pos: the position to which we are currently copying characters.
// element: Current array element.
// elements_end: Array end.
// separator: Separator string.
// Copy the separator to the result.
__ ldr(string_length, FieldMemOperand(separator, String::kLengthOffset));
__ SmiUntag(string_length);
__ add(string,
separator,
Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
__ CopyBytes(string, result_pos, string_length, scratch);
__ bind(&long_separator);
__ ldr(string, MemOperand(element, kPointerSize, PostIndex));
__ ldr(string_length, FieldMemOperand(string, String::kLengthOffset));
__ SmiUntag(string_length);
__ add(string,
string,
Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
__ CopyBytes(string, result_pos, string_length, scratch);
__ cmp(element, elements_end);
__ b(lt, &long_separator_loop); // End while (element < elements_end).
DCHECK(result.is(r0));
__ b(&done);
__ bind(&bailout);
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
__ bind(&done);
context()->Plug(r0);
}
void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
DCHECK(expr->arguments()->length() == 0);
ExternalReference debug_is_active =
......
......@@ -3221,6 +3221,226 @@ void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) {
}
void FullCodeGenerator::EmitFastOneByteArrayJoin(CallRuntime* expr) {
ASM_LOCATION("FullCodeGenerator::EmitFastOneByteArrayJoin");
ZoneList<Expression*>* args = expr->arguments();
DCHECK(args->length() == 2);
VisitForStackValue(args->at(1));
VisitForAccumulatorValue(args->at(0));
Register array = x0;
Register result = x0;
Register elements = x1;
Register element = x2;
Register separator = x3;
Register array_length = x4;
Register result_pos = x5;
Register map = x6;
Register string_length = x10;
Register elements_end = x11;
Register string = x12;
Register scratch1 = x13;
Register scratch2 = x14;
Register scratch3 = x7;
Register separator_length = x15;
Label bailout, done, one_char_separator, long_separator,
non_trivial_array, not_size_one_array, loop,
empty_separator_loop, one_char_separator_loop,
one_char_separator_loop_entry, long_separator_loop;
// The separator operand is on the stack.
__ Pop(separator);
// Check that the array is a JSArray.
__ JumpIfSmi(array, &bailout);
__ JumpIfNotObjectType(array, map, scratch1, JS_ARRAY_TYPE, &bailout);
// Check that the array has fast elements.
__ CheckFastElements(map, scratch1, &bailout);
// If the array has length zero, return the empty string.
// Load and untag the length of the array.
// It is an unsigned value, so we can skip sign extension.
// We assume little endianness.
__ Ldrsw(array_length,
UntagSmiFieldMemOperand(array, JSArray::kLengthOffset));
__ Cbnz(array_length, &non_trivial_array);
__ LoadRoot(result, Heap::kempty_stringRootIndex);
__ B(&done);
__ Bind(&non_trivial_array);
// Get the FixedArray containing array's elements.
__ Ldr(elements, FieldMemOperand(array, JSArray::kElementsOffset));
// Check that all array elements are sequential one-byte strings, and
// accumulate the sum of their lengths.
__ Mov(string_length, 0);
__ Add(element, elements, FixedArray::kHeaderSize - kHeapObjectTag);
__ Add(elements_end, element, Operand(array_length, LSL, kPointerSizeLog2));
// Loop condition: while (element < elements_end).
// Live values in registers:
// elements: Fixed array of strings.
// array_length: Length of the fixed array of strings (not smi)
// separator: Separator string
// string_length: Accumulated sum of string lengths (not smi).
// element: Current array element.
// elements_end: Array end.
if (FLAG_debug_code) {
__ Cmp(array_length, 0);
__ Assert(gt, kNoEmptyArraysHereInEmitFastOneByteArrayJoin);
}
__ Bind(&loop);
__ Ldr(string, MemOperand(element, kPointerSize, PostIndex));
__ JumpIfSmi(string, &bailout);
__ Ldr(scratch1, FieldMemOperand(string, HeapObject::kMapOffset));
__ Ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
__ JumpIfInstanceTypeIsNotSequentialOneByte(scratch1, scratch2, &bailout);
__ Ldrsw(scratch1,
UntagSmiFieldMemOperand(string, SeqOneByteString::kLengthOffset));
__ Adds(string_length, string_length, scratch1);
__ B(vs, &bailout);
__ Cmp(element, elements_end);
__ B(lt, &loop);
// If array_length is 1, return elements[0], a string.
__ Cmp(array_length, 1);
__ B(ne, &not_size_one_array);
__ Ldr(result, FieldMemOperand(elements, FixedArray::kHeaderSize));
__ B(&done);
__ Bind(&not_size_one_array);
// Live values in registers:
// separator: Separator string
// array_length: Length of the array (not smi).
// string_length: Sum of string lengths (not smi).
// elements: FixedArray of strings.
// Check that the separator is a flat one-byte string.
__ JumpIfSmi(separator, &bailout);
__ Ldr(scratch1, FieldMemOperand(separator, HeapObject::kMapOffset));
__ Ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
__ JumpIfInstanceTypeIsNotSequentialOneByte(scratch1, scratch2, &bailout);
// Add (separator length times array_length) - separator length to the
// string_length to get the length of the result string.
// Load the separator length as untagged.
// We assume little endianness, and that the length is positive.
__ Ldrsw(separator_length,
UntagSmiFieldMemOperand(separator,
SeqOneByteString::kLengthOffset));
__ Sub(string_length, string_length, separator_length);
__ Umaddl(string_length, array_length.W(), separator_length.W(),
string_length);
// Bailout for large object allocations.
__ Cmp(string_length, Page::kMaxRegularHeapObjectSize);
__ B(gt, &bailout);
// Get first element in the array.
__ Add(element, elements, FixedArray::kHeaderSize - kHeapObjectTag);
// Live values in registers:
// element: First array element
// separator: Separator string
// string_length: Length of result string (not smi)
// array_length: Length of the array (not smi).
__ AllocateOneByteString(result, string_length, scratch1, scratch2, scratch3,
&bailout);
// Prepare for looping. Set up elements_end to end of the array. Set
// result_pos to the position of the result where to write the first
// character.
// TODO(all): useless unless AllocateOneByteString trashes the register.
__ Add(elements_end, element, Operand(array_length, LSL, kPointerSizeLog2));
__ Add(result_pos, result, SeqOneByteString::kHeaderSize - kHeapObjectTag);
// Check the length of the separator.
__ Cmp(separator_length, 1);
__ B(eq, &one_char_separator);
__ B(gt, &long_separator);
// Empty separator case
__ Bind(&empty_separator_loop);
// Live values in registers:
// result_pos: the position to which we are currently copying characters.
// element: Current array element.
// elements_end: Array end.
// Copy next array element to the result.
__ Ldr(string, MemOperand(element, kPointerSize, PostIndex));
__ Ldrsw(string_length,
UntagSmiFieldMemOperand(string, String::kLengthOffset));
__ Add(string, string, SeqOneByteString::kHeaderSize - kHeapObjectTag);
__ CopyBytes(result_pos, string, string_length, scratch1);
__ Cmp(element, elements_end);
__ B(lt, &empty_separator_loop); // End while (element < elements_end).
__ B(&done);
// One-character separator case
__ Bind(&one_char_separator);
// Replace separator with its one-byte character value.
__ Ldrb(separator, FieldMemOperand(separator, SeqOneByteString::kHeaderSize));
// Jump into the loop after the code that copies the separator, so the first
// element is not preceded by a separator
__ B(&one_char_separator_loop_entry);
__ Bind(&one_char_separator_loop);
// Live values in registers:
// result_pos: the position to which we are currently copying characters.
// element: Current array element.
// elements_end: Array end.
// separator: Single separator one-byte char (in lower byte).
// Copy the separator character to the result.
__ Strb(separator, MemOperand(result_pos, 1, PostIndex));
// Copy next array element to the result.
__ Bind(&one_char_separator_loop_entry);
__ Ldr(string, MemOperand(element, kPointerSize, PostIndex));
__ Ldrsw(string_length,
UntagSmiFieldMemOperand(string, String::kLengthOffset));
__ Add(string, string, SeqOneByteString::kHeaderSize - kHeapObjectTag);
__ CopyBytes(result_pos, string, string_length, scratch1);
__ Cmp(element, elements_end);
__ B(lt, &one_char_separator_loop); // End while (element < elements_end).
__ B(&done);
// Long separator case (separator is more than one character). Entry is at the
// label long_separator below.
__ Bind(&long_separator_loop);
// Live values in registers:
// result_pos: the position to which we are currently copying characters.
// element: Current array element.
// elements_end: Array end.
// separator: Separator string.
// Copy the separator to the result.
// TODO(all): hoist next two instructions.
__ Ldrsw(string_length,
UntagSmiFieldMemOperand(separator, String::kLengthOffset));
__ Add(string, separator, SeqOneByteString::kHeaderSize - kHeapObjectTag);
__ CopyBytes(result_pos, string, string_length, scratch1);
__ Bind(&long_separator);
__ Ldr(string, MemOperand(element, kPointerSize, PostIndex));
__ Ldrsw(string_length,
UntagSmiFieldMemOperand(string, String::kLengthOffset));
__ Add(string, string, SeqOneByteString::kHeaderSize - kHeapObjectTag);
__ CopyBytes(result_pos, string, string_length, scratch1);
__ Cmp(element, elements_end);
__ B(lt, &long_separator_loop); // End while (element < elements_end).
__ B(&done);
__ Bind(&bailout);
// Returning undefined will force slower code to handle it.
__ LoadRoot(result, Heap::kUndefinedValueRootIndex);
__ Bind(&done);
context()->Plug(result);
}
void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
DCHECK(expr->arguments()->length() == 0);
ExternalReference debug_is_active =
......
......@@ -535,6 +535,7 @@ class FullCodeGenerator: public AstVisitor {
F(HasCachedArrayIndex) \
F(GetCachedArrayIndex) \
F(GetSuperConstructor) \
F(FastOneByteArrayJoin) \
F(GeneratorNext) \
F(GeneratorReturn) \
F(GeneratorThrow) \
......
......@@ -3304,6 +3304,275 @@ void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) {
}
void FullCodeGenerator::EmitFastOneByteArrayJoin(CallRuntime* expr) {
Label bailout, done, one_char_separator, long_separator,
non_trivial_array, not_size_one_array, loop,
loop_1, loop_1_condition, loop_2, loop_2_entry, loop_3, loop_3_entry;
ZoneList<Expression*>* args = expr->arguments();
DCHECK(args->length() == 2);
// We will leave the separator on the stack until the end of the function.
VisitForStackValue(args->at(1));
// Load this to eax (= array)
VisitForAccumulatorValue(args->at(0));
// All aliases of the same register have disjoint lifetimes.
Register array = eax;
Register elements = no_reg; // Will be eax.
Register index = edx;
Register string_length = ecx;
Register string = esi;
Register scratch = ebx;
Register array_length = edi;
Register result_pos = no_reg; // Will be edi.
// Separator operand is already pushed.
Operand separator_operand = Operand(esp, 2 * kPointerSize);
Operand result_operand = Operand(esp, 1 * kPointerSize);
Operand array_length_operand = Operand(esp, 0);
__ sub(esp, Immediate(2 * kPointerSize));
__ cld();
// Check that the array is a JSArray
__ JumpIfSmi(array, &bailout);
__ CmpObjectType(array, JS_ARRAY_TYPE, scratch);
__ j(not_equal, &bailout);
// Check that the array has fast elements.
__ CheckFastElements(scratch, &bailout);
// If the array has length zero, return the empty string.
__ mov(array_length, FieldOperand(array, JSArray::kLengthOffset));
__ SmiUntag(array_length);
__ j(not_zero, &non_trivial_array);
__ mov(result_operand, isolate()->factory()->empty_string());
__ jmp(&done);
// Save the array length.
__ bind(&non_trivial_array);
__ mov(array_length_operand, array_length);
// Save the FixedArray containing array's elements.
// End of array's live range.
elements = array;
__ mov(elements, FieldOperand(array, JSArray::kElementsOffset));
array = no_reg;
// Check that all array elements are sequential one-byte strings, and
// accumulate the sum of their lengths, as a smi-encoded value.
__ Move(index, Immediate(0));
__ Move(string_length, Immediate(0));
// Loop condition: while (index < length).
// Live loop registers: index, array_length, string,
// scratch, string_length, elements.
if (generate_debug_code_) {
__ cmp(index, array_length);
__ Assert(less, kNoEmptyArraysHereInEmitFastOneByteArrayJoin);
}
__ bind(&loop);
__ mov(string, FieldOperand(elements,
index,
times_pointer_size,
FixedArray::kHeaderSize));
__ JumpIfSmi(string, &bailout);
__ mov(scratch, FieldOperand(string, HeapObject::kMapOffset));
__ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
__ and_(scratch, Immediate(
kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
__ cmp(scratch, kStringTag | kOneByteStringTag | kSeqStringTag);
__ j(not_equal, &bailout);
__ add(string_length,
FieldOperand(string, SeqOneByteString::kLengthOffset));
__ j(overflow, &bailout);
__ add(index, Immediate(1));
__ cmp(index, array_length);
__ j(less, &loop);
// If array_length is 1, return elements[0], a string.
__ cmp(array_length, 1);
__ j(not_equal, &not_size_one_array);
__ mov(scratch, FieldOperand(elements, FixedArray::kHeaderSize));
__ mov(result_operand, scratch);
__ jmp(&done);
__ bind(&not_size_one_array);
// End of array_length live range.
result_pos = array_length;
array_length = no_reg;
// Live registers:
// string_length: Sum of string lengths, as a smi.
// elements: FixedArray of strings.
// Check that the separator is a flat one-byte string.
__ mov(string, separator_operand);
__ JumpIfSmi(string, &bailout);
__ mov(scratch, FieldOperand(string, HeapObject::kMapOffset));
__ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
__ and_(scratch, Immediate(
kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
__ cmp(scratch, kStringTag | kOneByteStringTag | kSeqStringTag);
__ j(not_equal, &bailout);
// Add (separator length times array_length) - separator length
// to string_length.
__ mov(scratch, separator_operand);
__ mov(scratch, FieldOperand(scratch, SeqOneByteString::kLengthOffset));
__ sub(string_length, scratch); // May be negative, temporarily.
__ imul(scratch, array_length_operand);
__ j(overflow, &bailout);
__ add(string_length, scratch);
__ j(overflow, &bailout);
__ shr(string_length, 1);
// Bailout for large object allocations.
__ cmp(string_length, Page::kMaxRegularHeapObjectSize);
__ j(greater, &bailout);
// Live registers and stack values:
// string_length
// elements
__ AllocateOneByteString(result_pos, string_length, scratch, index, string,
&bailout);
__ mov(result_operand, result_pos);
__ lea(result_pos, FieldOperand(result_pos, SeqOneByteString::kHeaderSize));
__ mov(string, separator_operand);
__ cmp(FieldOperand(string, SeqOneByteString::kLengthOffset),
Immediate(Smi::FromInt(1)));
__ j(equal, &one_char_separator);
__ j(greater, &long_separator);
// Empty separator case
__ mov(index, Immediate(0));
__ jmp(&loop_1_condition);
// Loop condition: while (index < length).
__ bind(&loop_1);
// Each iteration of the loop concatenates one string to the result.
// Live values in registers:
// index: which element of the elements array we are adding to the result.
// result_pos: the position to which we are currently copying characters.
// elements: the FixedArray of strings we are joining.
// Get string = array[index].
__ mov(string, FieldOperand(elements, index,
times_pointer_size,
FixedArray::kHeaderSize));
__ mov(string_length,
FieldOperand(string, String::kLengthOffset));
__ shr(string_length, 1);
__ lea(string,
FieldOperand(string, SeqOneByteString::kHeaderSize));
__ CopyBytes(string, result_pos, string_length, scratch);
__ add(index, Immediate(1));
__ bind(&loop_1_condition);
__ cmp(index, array_length_operand);
__ j(less, &loop_1); // End while (index < length).
__ jmp(&done);
// One-character separator case
__ bind(&one_char_separator);
// Replace separator with its one-byte character value.
__ mov_b(scratch, FieldOperand(string, SeqOneByteString::kHeaderSize));
__ mov_b(separator_operand, scratch);
__ Move(index, Immediate(0));
// Jump into the loop after the code that copies the separator, so the first
// element is not preceded by a separator
__ jmp(&loop_2_entry);
// Loop condition: while (index < length).
__ bind(&loop_2);
// Each iteration of the loop concatenates one string to the result.
// Live values in registers:
// index: which element of the elements array we are adding to the result.
// result_pos: the position to which we are currently copying characters.
// Copy the separator character to the result.
__ mov_b(scratch, separator_operand);
__ mov_b(Operand(result_pos, 0), scratch);
__ inc(result_pos);
__ bind(&loop_2_entry);
// Get string = array[index].
__ mov(string, FieldOperand(elements, index,
times_pointer_size,
FixedArray::kHeaderSize));
__ mov(string_length,
FieldOperand(string, String::kLengthOffset));
__ shr(string_length, 1);
__ lea(string,
FieldOperand(string, SeqOneByteString::kHeaderSize));
__ CopyBytes(string, result_pos, string_length, scratch);
__ add(index, Immediate(1));
__ cmp(index, array_length_operand);
__ j(less, &loop_2); // End while (index < length).
__ jmp(&done);
// Long separator case (separator is more than one character).
__ bind(&long_separator);
__ Move(index, Immediate(0));
// Jump into the loop after the code that copies the separator, so the first
// element is not preceded by a separator
__ jmp(&loop_3_entry);
// Loop condition: while (index < length).
__ bind(&loop_3);
// Each iteration of the loop concatenates one string to the result.
// Live values in registers:
// index: which element of the elements array we are adding to the result.
// result_pos: the position to which we are currently copying characters.
// Copy the separator to the result.
__ mov(string, separator_operand);
__ mov(string_length,
FieldOperand(string, String::kLengthOffset));
__ shr(string_length, 1);
__ lea(string,
FieldOperand(string, SeqOneByteString::kHeaderSize));
__ CopyBytes(string, result_pos, string_length, scratch);
__ bind(&loop_3_entry);
// Get string = array[index].
__ mov(string, FieldOperand(elements, index,
times_pointer_size,
FixedArray::kHeaderSize));
__ mov(string_length,
FieldOperand(string, String::kLengthOffset));
__ shr(string_length, 1);
__ lea(string,
FieldOperand(string, SeqOneByteString::kHeaderSize));
__ CopyBytes(string, result_pos, string_length, scratch);
__ add(index, Immediate(1));
__ cmp(index, array_length_operand);
__ j(less, &loop_3); // End while (index < length).
__ jmp(&done);
__ bind(&bailout);
__ mov(result_operand, isolate()->factory()->undefined_value());
__ bind(&done);
__ mov(eax, result_operand);
// Drop temp values from the stack, and restore context register.
__ add(esp, Immediate(3 * kPointerSize));
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
context()->Plug(eax);
}
void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
DCHECK(expr->arguments()->length() == 0);
ExternalReference debug_is_active =
......
......@@ -3416,6 +3416,238 @@ void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) {
}
void FullCodeGenerator::EmitFastOneByteArrayJoin(CallRuntime* expr) {
Label bailout, done, one_char_separator, long_separator,
non_trivial_array, not_size_one_array, loop,
empty_separator_loop, one_char_separator_loop,
one_char_separator_loop_entry, long_separator_loop;
ZoneList<Expression*>* args = expr->arguments();
DCHECK(args->length() == 2);
VisitForStackValue(args->at(1));
VisitForAccumulatorValue(args->at(0));
// All aliases of the same register have disjoint lifetimes.
Register array = v0;
Register elements = no_reg; // Will be v0.
Register result = no_reg; // Will be v0.
Register separator = a1;
Register array_length = a2;
Register result_pos = no_reg; // Will be a2.
Register string_length = a3;
Register string = t0;
Register element = t1;
Register elements_end = t2;
Register scratch1 = t3;
Register scratch2 = t5;
Register scratch3 = t4;
// Separator operand is on the stack.
__ pop(separator);
// Check that the array is a JSArray.
__ JumpIfSmi(array, &bailout);
__ GetObjectType(array, scratch1, scratch2);
__ Branch(&bailout, ne, scratch2, Operand(JS_ARRAY_TYPE));
// Check that the array has fast elements.
__ CheckFastElements(scratch1, scratch2, &bailout);
// If the array has length zero, return the empty string.
__ lw(array_length, FieldMemOperand(array, JSArray::kLengthOffset));
__ SmiUntag(array_length);
__ Branch(&non_trivial_array, ne, array_length, Operand(zero_reg));
__ LoadRoot(v0, Heap::kempty_stringRootIndex);
__ Branch(&done);
__ bind(&non_trivial_array);
// Get the FixedArray containing array's elements.
elements = array;
__ lw(elements, FieldMemOperand(array, JSArray::kElementsOffset));
array = no_reg; // End of array's live range.
// Check that all array elements are sequential one-byte strings, and
// accumulate the sum of their lengths, as a smi-encoded value.
__ mov(string_length, zero_reg);
__ Addu(element,
elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
__ Lsa(elements_end, element, array_length, kPointerSizeLog2);
// Loop condition: while (element < elements_end).
// Live values in registers:
// elements: Fixed array of strings.
// array_length: Length of the fixed array of strings (not smi)
// separator: Separator string
// string_length: Accumulated sum of string lengths (smi).
// element: Current array element.
// elements_end: Array end.
if (generate_debug_code_) {
__ Assert(gt, kNoEmptyArraysHereInEmitFastOneByteArrayJoin, array_length,
Operand(zero_reg));
}
__ bind(&loop);
__ lw(string, MemOperand(element));
__ Addu(element, element, kPointerSize);
__ JumpIfSmi(string, &bailout);
__ lw(scratch1, FieldMemOperand(string, HeapObject::kMapOffset));
__ lbu(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
__ JumpIfInstanceTypeIsNotSequentialOneByte(scratch1, scratch2, &bailout);
__ lw(scratch1, FieldMemOperand(string, SeqOneByteString::kLengthOffset));
__ AddBranchOvf(string_length, string_length, Operand(scratch1), &bailout);
__ Branch(&loop, lt, element, Operand(elements_end));
// If array_length is 1, return elements[0], a string.
__ Branch(&not_size_one_array, ne, array_length, Operand(1));
__ lw(v0, FieldMemOperand(elements, FixedArray::kHeaderSize));
__ Branch(&done);
__ bind(&not_size_one_array);
// Live values in registers:
// separator: Separator string
// array_length: Length of the array.
// string_length: Sum of string lengths (smi).
// elements: FixedArray of strings.
// Check that the separator is a flat one-byte string.
__ JumpIfSmi(separator, &bailout);
__ lw(scratch1, FieldMemOperand(separator, HeapObject::kMapOffset));
__ lbu(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
__ JumpIfInstanceTypeIsNotSequentialOneByte(scratch1, scratch2, &bailout);
// Add (separator length times array_length) - separator length to the
// string_length to get the length of the result string. array_length is not
// smi but the other values are, so the result is a smi.
__ lw(scratch1, FieldMemOperand(separator, SeqOneByteString::kLengthOffset));
__ Subu(string_length, string_length, Operand(scratch1));
__ Mul(scratch3, scratch2, array_length, scratch1);
// Check for smi overflow. No overflow if higher 33 bits of 64-bit result are
// zero.
__ Branch(&bailout, ne, scratch3, Operand(zero_reg));
__ And(scratch3, scratch2, Operand(0x80000000));
__ Branch(&bailout, ne, scratch3, Operand(zero_reg));
__ AddBranchOvf(string_length, string_length, Operand(scratch2), &bailout);
__ SmiUntag(string_length);
// Bailout for large object allocations.
__ Branch(&bailout, gt, string_length,
Operand(Page::kMaxRegularHeapObjectSize));
// Get first element in the array to free up the elements register to be used
// for the result.
__ Addu(element,
elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
result = elements; // End of live range for elements.
elements = no_reg;
// Live values in registers:
// element: First array element
// separator: Separator string
// string_length: Length of result string (not smi)
// array_length: Length of the array.
__ AllocateOneByteString(result, string_length, scratch1, scratch2,
elements_end, &bailout);
// Prepare for looping. Set up elements_end to end of the array. Set
// result_pos to the position of the result where to write the first
// character.
__ Lsa(elements_end, element, array_length, kPointerSizeLog2);
result_pos = array_length; // End of live range for array_length.
array_length = no_reg;
__ Addu(result_pos,
result,
Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
// Check the length of the separator.
__ lw(scratch1, FieldMemOperand(separator, SeqOneByteString::kLengthOffset));
__ li(at, Operand(Smi::FromInt(1)));
__ Branch(&one_char_separator, eq, scratch1, Operand(at));
__ Branch(&long_separator, gt, scratch1, Operand(at));
// Empty separator case.
__ bind(&empty_separator_loop);
// Live values in registers:
// result_pos: the position to which we are currently copying characters.
// element: Current array element.
// elements_end: Array end.
// Copy next array element to the result.
__ lw(string, MemOperand(element));
__ Addu(element, element, kPointerSize);
__ lw(string_length, FieldMemOperand(string, String::kLengthOffset));
__ SmiUntag(string_length);
__ Addu(string, string, SeqOneByteString::kHeaderSize - kHeapObjectTag);
__ CopyBytes(string, result_pos, string_length, scratch1);
// End while (element < elements_end).
__ Branch(&empty_separator_loop, lt, element, Operand(elements_end));
DCHECK(result.is(v0));
__ Branch(&done);
// One-character separator case.
__ bind(&one_char_separator);
// Replace separator with its one-byte character value.
__ lbu(separator, FieldMemOperand(separator, SeqOneByteString::kHeaderSize));
// Jump into the loop after the code that copies the separator, so the first
// element is not preceded by a separator.
__ jmp(&one_char_separator_loop_entry);
__ bind(&one_char_separator_loop);
// Live values in registers:
// result_pos: the position to which we are currently copying characters.
// element: Current array element.
// elements_end: Array end.
// separator: Single separator one-byte char (in lower byte).
// Copy the separator character to the result.
__ sb(separator, MemOperand(result_pos));
__ Addu(result_pos, result_pos, 1);
// Copy next array element to the result.
__ bind(&one_char_separator_loop_entry);
__ lw(string, MemOperand(element));
__ Addu(element, element, kPointerSize);
__ lw(string_length, FieldMemOperand(string, String::kLengthOffset));
__ SmiUntag(string_length);
__ Addu(string, string, SeqOneByteString::kHeaderSize - kHeapObjectTag);
__ CopyBytes(string, result_pos, string_length, scratch1);
// End while (element < elements_end).
__ Branch(&one_char_separator_loop, lt, element, Operand(elements_end));
DCHECK(result.is(v0));
__ Branch(&done);
// Long separator case (separator is more than one character). Entry is at the
// label long_separator below.
__ bind(&long_separator_loop);
// Live values in registers:
// result_pos: the position to which we are currently copying characters.
// element: Current array element.
// elements_end: Array end.
// separator: Separator string.
// Copy the separator to the result.
__ lw(string_length, FieldMemOperand(separator, String::kLengthOffset));
__ SmiUntag(string_length);
__ Addu(string,
separator,
Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
__ CopyBytes(string, result_pos, string_length, scratch1);
__ bind(&long_separator);
__ lw(string, MemOperand(element));
__ Addu(element, element, kPointerSize);
__ lw(string_length, FieldMemOperand(string, String::kLengthOffset));
__ SmiUntag(string_length);
__ Addu(string, string, SeqOneByteString::kHeaderSize - kHeapObjectTag);
__ CopyBytes(string, result_pos, string_length, scratch1);
// End while (element < elements_end).
__ Branch(&long_separator_loop, lt, element, Operand(elements_end));
DCHECK(result.is(v0));
__ Branch(&done);
__ bind(&bailout);
__ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
__ bind(&done);
context()->Plug(v0);
}
void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
DCHECK(expr->arguments()->length() == 0);
ExternalReference debug_is_active =
......
......@@ -3422,6 +3422,240 @@ void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) {
}
void FullCodeGenerator::EmitFastOneByteArrayJoin(CallRuntime* expr) {
Label bailout, done, one_char_separator, long_separator,
non_trivial_array, not_size_one_array, loop,
empty_separator_loop, one_char_separator_loop,
one_char_separator_loop_entry, long_separator_loop;
ZoneList<Expression*>* args = expr->arguments();
DCHECK(args->length() == 2);
VisitForStackValue(args->at(1));
VisitForAccumulatorValue(args->at(0));
// All aliases of the same register have disjoint lifetimes.
Register array = v0;
Register elements = no_reg; // Will be v0.
Register result = no_reg; // Will be v0.
Register separator = a1;
Register array_length = a2;
Register result_pos = no_reg; // Will be a2.
Register string_length = a3;
Register string = a4;
Register element = a5;
Register elements_end = a6;
Register scratch1 = a7;
Register scratch2 = t1;
Register scratch3 = t0;
// Separator operand is on the stack.
__ pop(separator);
// Check that the array is a JSArray.
__ JumpIfSmi(array, &bailout);
__ GetObjectType(array, scratch1, scratch2);
__ Branch(&bailout, ne, scratch2, Operand(JS_ARRAY_TYPE));
// Check that the array has fast elements.
__ CheckFastElements(scratch1, scratch2, &bailout);
// If the array has length zero, return the empty string.
__ ld(array_length, FieldMemOperand(array, JSArray::kLengthOffset));
__ SmiUntag(array_length);
__ Branch(&non_trivial_array, ne, array_length, Operand(zero_reg));
__ LoadRoot(v0, Heap::kempty_stringRootIndex);
__ Branch(&done);
__ bind(&non_trivial_array);
// Get the FixedArray containing array's elements.
elements = array;
__ ld(elements, FieldMemOperand(array, JSArray::kElementsOffset));
array = no_reg; // End of array's live range.
// Check that all array elements are sequential one-byte strings, and
// accumulate the sum of their lengths, as a smi-encoded value.
__ mov(string_length, zero_reg);
__ Daddu(element,
elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
__ Dlsa(elements_end, element, array_length, kPointerSizeLog2);
// Loop condition: while (element < elements_end).
// Live values in registers:
// elements: Fixed array of strings.
// array_length: Length of the fixed array of strings (not smi)
// separator: Separator string
// string_length: Accumulated sum of string lengths (smi).
// element: Current array element.
// elements_end: Array end.
if (generate_debug_code_) {
__ Assert(gt, kNoEmptyArraysHereInEmitFastOneByteArrayJoin, array_length,
Operand(zero_reg));
}
__ bind(&loop);
__ ld(string, MemOperand(element));
__ Daddu(element, element, kPointerSize);
__ JumpIfSmi(string, &bailout);
__ ld(scratch1, FieldMemOperand(string, HeapObject::kMapOffset));
__ lbu(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
__ JumpIfInstanceTypeIsNotSequentialOneByte(scratch1, scratch2, &bailout);
__ ld(scratch1, FieldMemOperand(string, SeqOneByteString::kLengthOffset));
__ DadduAndCheckForOverflow(string_length, string_length, scratch1, scratch3);
__ BranchOnOverflow(&bailout, scratch3);
__ Branch(&loop, lt, element, Operand(elements_end));
// If array_length is 1, return elements[0], a string.
__ Branch(&not_size_one_array, ne, array_length, Operand(1));
__ ld(v0, FieldMemOperand(elements, FixedArray::kHeaderSize));
__ Branch(&done);
__ bind(&not_size_one_array);
// Live values in registers:
// separator: Separator string
// array_length: Length of the array.
// string_length: Sum of string lengths (smi).
// elements: FixedArray of strings.
// Check that the separator is a flat one-byte string.
__ JumpIfSmi(separator, &bailout);
__ ld(scratch1, FieldMemOperand(separator, HeapObject::kMapOffset));
__ lbu(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
__ JumpIfInstanceTypeIsNotSequentialOneByte(scratch1, scratch2, &bailout);
// Add (separator length times array_length) - separator length to the
// string_length to get the length of the result string. array_length is not
// smi but the other values are, so the result is a smi.
__ ld(scratch1, FieldMemOperand(separator, SeqOneByteString::kLengthOffset));
__ Dsubu(string_length, string_length, Operand(scratch1));
__ SmiUntag(scratch1);
__ Dmul(scratch2, array_length, scratch1);
// Check for smi overflow. No overflow if higher 33 bits of 64-bit result are
// zero.
__ dsra32(scratch1, scratch2, 0);
__ Branch(&bailout, ne, scratch2, Operand(zero_reg));
__ SmiUntag(string_length);
__ AdduAndCheckForOverflow(string_length, string_length, scratch2, scratch3);
__ BranchOnOverflow(&bailout, scratch3);
// Bailout for large object allocations.
__ Branch(&bailout, gt, string_length,
Operand(Page::kMaxRegularHeapObjectSize));
// Get first element in the array to free up the elements register to be used
// for the result.
__ Daddu(element,
elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
result = elements; // End of live range for elements.
elements = no_reg;
// Live values in registers:
// element: First array element
// separator: Separator string
// string_length: Length of result string (not smi)
// array_length: Length of the array.
__ AllocateOneByteString(result, string_length, scratch1, scratch2,
elements_end, &bailout);
// Prepare for looping. Set up elements_end to end of the array. Set
// result_pos to the position of the result where to write the first
// character.
__ Dlsa(elements_end, element, array_length, kPointerSizeLog2);
result_pos = array_length; // End of live range for array_length.
array_length = no_reg;
__ Daddu(result_pos,
result,
Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
// Check the length of the separator.
__ ld(scratch1, FieldMemOperand(separator, SeqOneByteString::kLengthOffset));
__ li(at, Operand(Smi::FromInt(1)));
__ Branch(&one_char_separator, eq, scratch1, Operand(at));
__ Branch(&long_separator, gt, scratch1, Operand(at));
// Empty separator case.
__ bind(&empty_separator_loop);
// Live values in registers:
// result_pos: the position to which we are currently copying characters.
// element: Current array element.
// elements_end: Array end.
// Copy next array element to the result.
__ ld(string, MemOperand(element));
__ Daddu(element, element, kPointerSize);
__ ld(string_length, FieldMemOperand(string, String::kLengthOffset));
__ SmiUntag(string_length);
__ Daddu(string, string, SeqOneByteString::kHeaderSize - kHeapObjectTag);
__ CopyBytes(string, result_pos, string_length, scratch1);
// End while (element < elements_end).
__ Branch(&empty_separator_loop, lt, element, Operand(elements_end));
DCHECK(result.is(v0));
__ Branch(&done);
// One-character separator case.
__ bind(&one_char_separator);
// Replace separator with its one-byte character value.
__ lbu(separator, FieldMemOperand(separator, SeqOneByteString::kHeaderSize));
// Jump into the loop after the code that copies the separator, so the first
// element is not preceded by a separator.
__ jmp(&one_char_separator_loop_entry);
__ bind(&one_char_separator_loop);
// Live values in registers:
// result_pos: the position to which we are currently copying characters.
// element: Current array element.
// elements_end: Array end.
// separator: Single separator one-byte char (in lower byte).
// Copy the separator character to the result.
__ sb(separator, MemOperand(result_pos));
__ Daddu(result_pos, result_pos, 1);
// Copy next array element to the result.
__ bind(&one_char_separator_loop_entry);
__ ld(string, MemOperand(element));
__ Daddu(element, element, kPointerSize);
__ ld(string_length, FieldMemOperand(string, String::kLengthOffset));
__ SmiUntag(string_length);
__ Daddu(string, string, SeqOneByteString::kHeaderSize - kHeapObjectTag);
__ CopyBytes(string, result_pos, string_length, scratch1);
// End while (element < elements_end).
__ Branch(&one_char_separator_loop, lt, element, Operand(elements_end));
DCHECK(result.is(v0));
__ Branch(&done);
// Long separator case (separator is more than one character). Entry is at the
// label long_separator below.
__ bind(&long_separator_loop);
// Live values in registers:
// result_pos: the position to which we are currently copying characters.
// element: Current array element.
// elements_end: Array end.
// separator: Separator string.
// Copy the separator to the result.
__ ld(string_length, FieldMemOperand(separator, String::kLengthOffset));
__ SmiUntag(string_length);
__ Daddu(string,
separator,
Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
__ CopyBytes(string, result_pos, string_length, scratch1);
__ bind(&long_separator);
__ ld(string, MemOperand(element));
__ Daddu(element, element, kPointerSize);
__ ld(string_length, FieldMemOperand(string, String::kLengthOffset));
__ SmiUntag(string_length);
__ Daddu(string, string, SeqOneByteString::kHeaderSize - kHeapObjectTag);
__ CopyBytes(string, result_pos, string_length, scratch1);
// End while (element < elements_end).
__ Branch(&long_separator_loop, lt, element, Operand(elements_end));
DCHECK(result.is(v0));
__ Branch(&done);
__ bind(&bailout);
__ LoadRoot(v0, Heap::kUndefinedValueRootIndex);
__ bind(&done);
context()->Plug(v0);
}
void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
DCHECK(expr->arguments()->length() == 0);
ExternalReference debug_is_active =
......
This diff is collapsed.
This diff is collapsed.
......@@ -3296,6 +3296,275 @@ void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) {
}
void FullCodeGenerator::EmitFastOneByteArrayJoin(CallRuntime* expr) {
Label bailout, done, one_char_separator, long_separator,
non_trivial_array, not_size_one_array, loop,
loop_1, loop_1_condition, loop_2, loop_2_entry, loop_3, loop_3_entry;
ZoneList<Expression*>* args = expr->arguments();
DCHECK(args->length() == 2);
// We will leave the separator on the stack until the end of the function.
VisitForStackValue(args->at(1));
// Load this to eax (= array)
VisitForAccumulatorValue(args->at(0));
// All aliases of the same register have disjoint lifetimes.
Register array = eax;
Register elements = no_reg; // Will be eax.
Register index = edx;
Register string_length = ecx;
Register string = esi;
Register scratch = ebx;
Register array_length = edi;
Register result_pos = no_reg; // Will be edi.
// Separator operand is already pushed.
Operand separator_operand = Operand(esp, 2 * kPointerSize);
Operand result_operand = Operand(esp, 1 * kPointerSize);
Operand array_length_operand = Operand(esp, 0);
__ sub(esp, Immediate(2 * kPointerSize));
__ cld();
// Check that the array is a JSArray
__ JumpIfSmi(array, &bailout);
__ CmpObjectType(array, JS_ARRAY_TYPE, scratch);
__ j(not_equal, &bailout);
// Check that the array has fast elements.
__ CheckFastElements(scratch, &bailout);
// If the array has length zero, return the empty string.
__ mov(array_length, FieldOperand(array, JSArray::kLengthOffset));
__ SmiUntag(array_length);
__ j(not_zero, &non_trivial_array);
__ mov(result_operand, isolate()->factory()->empty_string());
__ jmp(&done);
// Save the array length.
__ bind(&non_trivial_array);
__ mov(array_length_operand, array_length);
// Save the FixedArray containing array's elements.
// End of array's live range.
elements = array;
__ mov(elements, FieldOperand(array, JSArray::kElementsOffset));
array = no_reg;
// Check that all array elements are sequential one-byte strings, and
// accumulate the sum of their lengths, as a smi-encoded value.
__ Move(index, Immediate(0));
__ Move(string_length, Immediate(0));
// Loop condition: while (index < length).
// Live loop registers: index, array_length, string,
// scratch, string_length, elements.
if (generate_debug_code_) {
__ cmp(index, array_length);
__ Assert(less, kNoEmptyArraysHereInEmitFastOneByteArrayJoin);
}
__ bind(&loop);
__ mov(string, FieldOperand(elements,
index,
times_pointer_size,
FixedArray::kHeaderSize));
__ JumpIfSmi(string, &bailout);
__ mov(scratch, FieldOperand(string, HeapObject::kMapOffset));
__ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
__ and_(scratch, Immediate(
kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
__ cmp(scratch, kStringTag | kOneByteStringTag | kSeqStringTag);
__ j(not_equal, &bailout);
__ add(string_length,
FieldOperand(string, SeqOneByteString::kLengthOffset));
__ j(overflow, &bailout);
__ add(index, Immediate(1));
__ cmp(index, array_length);
__ j(less, &loop);
// If array_length is 1, return elements[0], a string.
__ cmp(array_length, 1);
__ j(not_equal, &not_size_one_array);
__ mov(scratch, FieldOperand(elements, FixedArray::kHeaderSize));
__ mov(result_operand, scratch);
__ jmp(&done);
__ bind(&not_size_one_array);
// End of array_length live range.
result_pos = array_length;
array_length = no_reg;
// Live registers:
// string_length: Sum of string lengths, as a smi.
// elements: FixedArray of strings.
// Check that the separator is a flat one-byte string.
__ mov(string, separator_operand);
__ JumpIfSmi(string, &bailout);
__ mov(scratch, FieldOperand(string, HeapObject::kMapOffset));
__ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
__ and_(scratch, Immediate(
kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
__ cmp(scratch, kStringTag | kOneByteStringTag | kSeqStringTag);
__ j(not_equal, &bailout);
// Add (separator length times array_length) - separator length
// to string_length.
__ mov(scratch, separator_operand);
__ mov(scratch, FieldOperand(scratch, SeqOneByteString::kLengthOffset));
__ sub(string_length, scratch); // May be negative, temporarily.
__ imul(scratch, array_length_operand);
__ j(overflow, &bailout);
__ add(string_length, scratch);
__ j(overflow, &bailout);
__ shr(string_length, 1);
// Bailout for large object allocations.
__ cmp(string_length, Page::kMaxRegularHeapObjectSize);
__ j(greater, &bailout);
// Live registers and stack values:
// string_length
// elements
__ AllocateOneByteString(result_pos, string_length, scratch, index, string,
&bailout);
__ mov(result_operand, result_pos);
__ lea(result_pos, FieldOperand(result_pos, SeqOneByteString::kHeaderSize));
__ mov(string, separator_operand);
__ cmp(FieldOperand(string, SeqOneByteString::kLengthOffset),
Immediate(Smi::FromInt(1)));
__ j(equal, &one_char_separator);
__ j(greater, &long_separator);
// Empty separator case
__ mov(index, Immediate(0));
__ jmp(&loop_1_condition);
// Loop condition: while (index < length).
__ bind(&loop_1);
// Each iteration of the loop concatenates one string to the result.
// Live values in registers:
// index: which element of the elements array we are adding to the result.
// result_pos: the position to which we are currently copying characters.
// elements: the FixedArray of strings we are joining.
// Get string = array[index].
__ mov(string, FieldOperand(elements, index,
times_pointer_size,
FixedArray::kHeaderSize));
__ mov(string_length,
FieldOperand(string, String::kLengthOffset));
__ shr(string_length, 1);
__ lea(string,
FieldOperand(string, SeqOneByteString::kHeaderSize));
__ CopyBytes(string, result_pos, string_length, scratch);
__ add(index, Immediate(1));
__ bind(&loop_1_condition);
__ cmp(index, array_length_operand);
__ j(less, &loop_1); // End while (index < length).
__ jmp(&done);
// One-character separator case
__ bind(&one_char_separator);
// Replace separator with its one-byte character value.
__ mov_b(scratch, FieldOperand(string, SeqOneByteString::kHeaderSize));
__ mov_b(separator_operand, scratch);
__ Move(index, Immediate(0));
// Jump into the loop after the code that copies the separator, so the first
// element is not preceded by a separator
__ jmp(&loop_2_entry);
// Loop condition: while (index < length).
__ bind(&loop_2);
// Each iteration of the loop concatenates one string to the result.
// Live values in registers:
// index: which element of the elements array we are adding to the result.
// result_pos: the position to which we are currently copying characters.
// Copy the separator character to the result.
__ mov_b(scratch, separator_operand);
__ mov_b(Operand(result_pos, 0), scratch);
__ inc(result_pos);
__ bind(&loop_2_entry);
// Get string = array[index].
__ mov(string, FieldOperand(elements, index,
times_pointer_size,
FixedArray::kHeaderSize));
__ mov(string_length,
FieldOperand(string, String::kLengthOffset));
__ shr(string_length, 1);
__ lea(string,
FieldOperand(string, SeqOneByteString::kHeaderSize));
__ CopyBytes(string, result_pos, string_length, scratch);
__ add(index, Immediate(1));
__ cmp(index, array_length_operand);
__ j(less, &loop_2); // End while (index < length).
__ jmp(&done);
// Long separator case (separator is more than one character).
__ bind(&long_separator);
__ Move(index, Immediate(0));
// Jump into the loop after the code that copies the separator, so the first
// element is not preceded by a separator
__ jmp(&loop_3_entry);
// Loop condition: while (index < length).
__ bind(&loop_3);
// Each iteration of the loop concatenates one string to the result.
// Live values in registers:
// index: which element of the elements array we are adding to the result.
// result_pos: the position to which we are currently copying characters.
// Copy the separator to the result.
__ mov(string, separator_operand);
__ mov(string_length,
FieldOperand(string, String::kLengthOffset));
__ shr(string_length, 1);
__ lea(string,
FieldOperand(string, SeqOneByteString::kHeaderSize));
__ CopyBytes(string, result_pos, string_length, scratch);
__ bind(&loop_3_entry);
// Get string = array[index].
__ mov(string, FieldOperand(elements, index,
times_pointer_size,
FixedArray::kHeaderSize));
__ mov(string_length,
FieldOperand(string, String::kLengthOffset));
__ shr(string_length, 1);
__ lea(string,
FieldOperand(string, SeqOneByteString::kHeaderSize));
__ CopyBytes(string, result_pos, string_length, scratch);
__ add(index, Immediate(1));
__ cmp(index, array_length_operand);
__ j(less, &loop_3); // End while (index < length).
__ jmp(&done);
__ bind(&bailout);
__ mov(result_operand, isolate()->factory()->undefined_value());
__ bind(&done);
__ mov(eax, result_operand);
// Drop temp values from the stack, and restore context register.
__ add(esp, Immediate(3 * kPointerSize));
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
context()->Plug(eax);
}
void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
DCHECK(expr->arguments()->length() == 0);
ExternalReference debug_is_active =
......
......@@ -209,6 +209,8 @@ function Join(array, length, separator, convert) {
elements[elements_length++] = e;
}
elements.length = elements_length;
var result = %_FastOneByteArrayJoin(elements, '');
if (!IS_UNDEFINED(result)) return result;
return %StringBuilderConcat(elements, elements_length, '');
}
// Non-empty separator case.
......@@ -231,6 +233,9 @@ function Join(array, length, separator, convert) {
elements[i] = e;
}
}
var result = %_FastOneByteArrayJoin(elements, separator);
if (!IS_UNDEFINED(result)) return result;
return %StringBuilderJoin(elements, length, separator);
} finally {
// Make sure to remove the last element of the visited array no
......@@ -442,6 +447,9 @@ function InnerArrayJoin(separator, array, length) {
separator = TO_STRING(separator);
}
var result = %_FastOneByteArrayJoin(array, separator);
if (!IS_UNDEFINED(result)) return result;
// Fast case for one-element arrays.
if (length === 1) {
var e = array[0];
......
......@@ -489,6 +489,15 @@ RUNTIME_FUNCTION(Runtime_GetCachedArrayIndex) {
}
RUNTIME_FUNCTION(Runtime_FastOneByteArrayJoin) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 2);
// Returning undefined means that this fast path fails and one has to resort
// to a slow path.
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_ArraySpeciesConstructor) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
......
......@@ -51,6 +51,7 @@ namespace internal {
F(GetCachedArrayIndex, 1, 1) \
F(FixedArrayGet, 2, 1) \
F(FixedArraySet, 3, 1) \
F(FastOneByteArrayJoin, 2, 1) \
F(ArraySpeciesConstructor, 1, 1)
......
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