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 =
......
......@@ -3395,6 +3395,261 @@ 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 = r3;
Register elements = no_reg; // Will be r3.
Register result = no_reg; // Will be r3.
Register separator = r4;
Register array_length = r5;
Register result_pos = no_reg; // Will be r5
Register string_length = r6;
Register string = r7;
Register element = r8;
Register elements_end = r9;
Register scratch1 = r10;
Register scratch2 = r11;
// Separator operand is on the stack.
__ pop(separator);
// Check that the array is a JSArray.
__ JumpIfSmi(array, &bailout);
__ CompareObjectType(array, scratch1, scratch2, JS_ARRAY_TYPE);
__ bne(&bailout);
// Check that the array has fast elements.
__ CheckFastElements(scratch1, scratch2, &bailout);
// If the array has length zero, return the empty string.
__ LoadP(array_length, FieldMemOperand(array, JSArray::kLengthOffset));
__ SmiUntag(array_length);
__ cmpi(array_length, Operand::Zero());
__ bne(&non_trivial_array);
__ LoadRoot(r3, Heap::kempty_stringRootIndex);
__ b(&done);
__ bind(&non_trivial_array);
// Get the FixedArray containing array's elements.
elements = array;
__ LoadP(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.
__ li(string_length, Operand::Zero());
__ addi(element, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
__ ShiftLeftImm(elements_end, array_length, Operand(kPointerSizeLog2));
__ add(elements_end, element, elements_end);
// 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_) {
__ cmpi(array_length, Operand::Zero());
__ Assert(gt, kNoEmptyArraysHereInEmitFastOneByteArrayJoin);
}
__ bind(&loop);
__ LoadP(string, MemOperand(element));
__ addi(element, element, Operand(kPointerSize));
__ JumpIfSmi(string, &bailout);
__ LoadP(scratch1, FieldMemOperand(string, HeapObject::kMapOffset));
__ lbz(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
__ JumpIfInstanceTypeIsNotSequentialOneByte(scratch1, scratch2, &bailout);
__ LoadP(scratch1, FieldMemOperand(string, SeqOneByteString::kLengthOffset));
__ AddAndCheckForOverflow(string_length, string_length, scratch1, scratch2,
r0);
__ BranchOnOverflow(&bailout);
__ cmp(element, elements_end);
__ blt(&loop);
// If array_length is 1, return elements[0], a string.
__ cmpi(array_length, Operand(1));
__ bne(&not_size_one_array);
__ LoadP(r3, 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);
__ LoadP(scratch1, FieldMemOperand(separator, HeapObject::kMapOffset));
__ lbz(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.
__ LoadP(scratch1,
FieldMemOperand(separator, SeqOneByteString::kLengthOffset));
__ sub(string_length, string_length, scratch1);
#if V8_TARGET_ARCH_PPC64
__ SmiUntag(scratch1, scratch1);
__ Mul(scratch2, array_length, scratch1);
// Check for smi overflow. No overflow if higher 33 bits of 64-bit result are
// zero.
__ ShiftRightImm(ip, scratch2, Operand(31), SetRC);
__ bne(&bailout, cr0);
__ SmiTag(scratch2, scratch2);
#else
// array_length is not smi but the other values are, so the result is a smi
__ mullw(scratch2, array_length, scratch1);
__ mulhw(ip, array_length, scratch1);
// Check for smi overflow. No overflow if higher 33 bits of 64-bit result are
// zero.
__ cmpi(ip, Operand::Zero());
__ bne(&bailout);
__ cmpwi(scratch2, Operand::Zero());
__ blt(&bailout);
#endif
__ AddAndCheckForOverflow(string_length, string_length, scratch2, scratch1,
r0);
__ BranchOnOverflow(&bailout);
__ SmiUntag(string_length);
// Bailout for large object allocations.
__ Cmpi(string_length, Operand(Page::kMaxRegularHeapObjectSize), r0);
__ bgt(&bailout);
// Get first element in the array to free up the elements register to be used
// for the result.
__ addi(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.
__ ShiftLeftImm(elements_end, array_length, Operand(kPointerSizeLog2));
__ add(elements_end, element, elements_end);
result_pos = array_length; // End of live range for array_length.
array_length = no_reg;
__ addi(result_pos, result,
Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
// Check the length of the separator.
__ LoadP(scratch1,
FieldMemOperand(separator, SeqOneByteString::kLengthOffset));
__ CmpSmiLiteral(scratch1, Smi::FromInt(1), r0);
__ beq(&one_char_separator);
__ bgt(&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.
__ LoadP(string, MemOperand(element));
__ addi(element, element, Operand(kPointerSize));
__ LoadP(string_length, FieldMemOperand(string, String::kLengthOffset));
__ SmiUntag(string_length);
__ addi(string, string,
Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
__ CopyBytes(string, result_pos, string_length, scratch1);
__ cmp(element, elements_end);
__ blt(&empty_separator_loop); // End while (element < elements_end).
DCHECK(result.is(r3));
__ b(&done);
// One-character separator case
__ bind(&one_char_separator);
// Replace separator with its one-byte character value.
__ lbz(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.
__ stb(separator, MemOperand(result_pos));
__ addi(result_pos, result_pos, Operand(1));
// Copy next array element to the result.
__ bind(&one_char_separator_loop_entry);
__ LoadP(string, MemOperand(element));
__ addi(element, element, Operand(kPointerSize));
__ LoadP(string_length, FieldMemOperand(string, String::kLengthOffset));
__ SmiUntag(string_length);
__ addi(string, string,
Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
__ CopyBytes(string, result_pos, string_length, scratch1);
__ cmpl(element, elements_end);
__ blt(&one_char_separator_loop); // End while (element < elements_end).
DCHECK(result.is(r3));
__ 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.
__ LoadP(string_length, FieldMemOperand(separator, String::kLengthOffset));
__ SmiUntag(string_length);
__ addi(string, separator,
Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
__ CopyBytes(string, result_pos, string_length, scratch1);
__ bind(&long_separator);
__ LoadP(string, MemOperand(element));
__ addi(element, element, Operand(kPointerSize));
__ LoadP(string_length, FieldMemOperand(string, String::kLengthOffset));
__ SmiUntag(string_length);
__ addi(string, string,
Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
__ CopyBytes(string, result_pos, string_length, scratch1);
__ cmpl(element, elements_end);
__ blt(&long_separator_loop); // End while (element < elements_end).
DCHECK(result.is(r3));
__ b(&done);
__ bind(&bailout);
__ LoadRoot(r3, Heap::kUndefinedValueRootIndex);
__ bind(&done);
context()->Plug(r3);
}
void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
DCHECK(expr->arguments()->length() == 0);
ExternalReference debug_is_active =
......
......@@ -3287,6 +3287,296 @@ void FullCodeGenerator::EmitGetSuperConstructor(CallRuntime* expr) {
}
void FullCodeGenerator::EmitFastOneByteArrayJoin(CallRuntime* expr) {
Label bailout, return_result, 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 rax (= array)
VisitForAccumulatorValue(args->at(0));
// All aliases of the same register have disjoint lifetimes.
Register array = rax;
Register elements = no_reg; // Will be rax.
Register index = rdx;
Register string_length = rcx;
Register string = rsi;
Register scratch = rbx;
Register array_length = rdi;
Register result_pos = no_reg; // Will be rdi.
Operand separator_operand = Operand(rsp, 2 * kPointerSize);
Operand result_operand = Operand(rsp, 1 * kPointerSize);
Operand array_length_operand = Operand(rsp, 0 * kPointerSize);
// Separator operand is already pushed. Make room for the two
// other stack fields, and clear the direction flag in anticipation
// of calling CopyBytes.
__ subp(rsp, 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);
// Array has fast elements, so its length must be a smi.
// If the array has length zero, return the empty string.
__ movp(array_length, FieldOperand(array, JSArray::kLengthOffset));
__ SmiCompare(array_length, Smi::FromInt(0));
__ j(not_zero, &non_trivial_array);
__ LoadRoot(rax, Heap::kempty_stringRootIndex);
__ jmp(&return_result);
// Save the array length on the stack.
__ bind(&non_trivial_array);
__ SmiToInteger32(array_length, array_length);
__ movl(array_length_operand, array_length);
// Save the FixedArray containing array's elements.
// End of array's live range.
elements = array;
__ movp(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.
__ Set(index, 0);
__ Set(string_length, 0);
// Loop condition: while (index < array_length).
// Live loop registers: index(int32), array_length(int32), string(String*),
// scratch, string_length(int32), elements(FixedArray*).
if (generate_debug_code_) {
__ cmpp(index, array_length);
__ Assert(below, kNoEmptyArraysHereInEmitFastOneByteArrayJoin);
}
__ bind(&loop);
__ movp(string, FieldOperand(elements,
index,
times_pointer_size,
FixedArray::kHeaderSize));
__ JumpIfSmi(string, &bailout);
__ movp(scratch, FieldOperand(string, HeapObject::kMapOffset));
__ movzxbl(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
__ andb(scratch, Immediate(
kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
__ cmpb(scratch, Immediate(kStringTag | kOneByteStringTag | kSeqStringTag));
__ j(not_equal, &bailout);
__ AddSmiField(string_length,
FieldOperand(string, SeqOneByteString::kLengthOffset));
__ j(overflow, &bailout);
__ incl(index);
__ cmpl(index, array_length);
__ j(less, &loop);
// Live registers:
// string_length: Sum of string lengths.
// elements: FixedArray of strings.
// index: Array length.
// array_length: Array length.
// If array_length is 1, return elements[0], a string.
__ cmpl(array_length, Immediate(1));
__ j(not_equal, &not_size_one_array);
__ movp(rax, FieldOperand(elements, FixedArray::kHeaderSize));
__ jmp(&return_result);
__ 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.
// elements: FixedArray of strings.
// index: Array length.
// Check that the separator is a sequential one-byte string.
__ movp(string, separator_operand);
__ JumpIfSmi(string, &bailout);
__ movp(scratch, FieldOperand(string, HeapObject::kMapOffset));
__ movzxbl(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset));
__ andb(scratch, Immediate(
kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask));
__ cmpb(scratch, Immediate(kStringTag | kOneByteStringTag | kSeqStringTag));
__ j(not_equal, &bailout);
// Live registers:
// string_length: Sum of string lengths.
// elements: FixedArray of strings.
// index: Array length.
// string: Separator string.
// Add (separator length times (array_length - 1)) to string_length.
__ SmiToInteger32(scratch,
FieldOperand(string, SeqOneByteString::kLengthOffset));
__ decl(index);
__ imull(scratch, index);
__ j(overflow, &bailout);
__ addl(string_length, scratch);
__ j(overflow, &bailout);
__ jmp(&bailout);
// Bailout for large object allocations.
__ cmpl(string_length, Immediate(Page::kMaxRegularHeapObjectSize));
__ j(greater, &bailout);
// Live registers and stack values:
// string_length: Total length of result string.
// elements: FixedArray of strings.
__ AllocateOneByteString(result_pos, string_length, scratch, index, string,
&bailout);
__ movp(result_operand, result_pos);
__ leap(result_pos, FieldOperand(result_pos, SeqOneByteString::kHeaderSize));
__ movp(string, separator_operand);
__ SmiCompare(FieldOperand(string, SeqOneByteString::kLengthOffset),
Smi::FromInt(1));
__ j(equal, &one_char_separator);
__ j(greater, &long_separator);
// Empty separator case:
__ Set(index, 0);
__ movl(scratch, array_length_operand);
__ jmp(&loop_1_condition);
// Loop condition: while (index < array_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.
// scratch: array length.
// Get string = array[index].
__ movp(string, FieldOperand(elements, index,
times_pointer_size,
FixedArray::kHeaderSize));
__ SmiToInteger32(string_length,
FieldOperand(string, String::kLengthOffset));
__ leap(string,
FieldOperand(string, SeqOneByteString::kHeaderSize));
__ CopyBytes(result_pos, string, string_length);
__ incl(index);
__ bind(&loop_1_condition);
__ cmpl(index, scratch);
__ j(less, &loop_1); // Loop while (index < array_length).
__ jmp(&done);
// Generic bailout code used from several places.
__ bind(&bailout);
__ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
__ jmp(&return_result);
// One-character separator case
__ bind(&one_char_separator);
// Get the separator one-byte character value.
// Register "string" holds the separator.
__ movzxbl(scratch, FieldOperand(string, SeqOneByteString::kHeaderSize));
__ Set(index, 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:
// elements: The FixedArray of strings we are joining.
// index: which element of the elements array we are adding to the result.
// result_pos: the position to which we are currently copying characters.
// scratch: Separator character.
// Copy the separator character to the result.
__ movb(Operand(result_pos, 0), scratch);
__ incp(result_pos);
__ bind(&loop_2_entry);
// Get string = array[index].
__ movp(string, FieldOperand(elements, index,
times_pointer_size,
FixedArray::kHeaderSize));
__ SmiToInteger32(string_length,
FieldOperand(string, String::kLengthOffset));
__ leap(string,
FieldOperand(string, SeqOneByteString::kHeaderSize));
__ CopyBytes(result_pos, string, string_length);
__ incl(index);
__ cmpl(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);
// Make elements point to end of elements array, and index
// count from -array_length to zero, so we don't need to maintain
// a loop limit.
__ movl(index, array_length_operand);
__ leap(elements, FieldOperand(elements, index, times_pointer_size,
FixedArray::kHeaderSize));
__ negq(index);
// Replace separator string with pointer to its first character, and
// make scratch be its length.
__ movp(string, separator_operand);
__ SmiToInteger32(scratch,
FieldOperand(string, String::kLengthOffset));
__ leap(string,
FieldOperand(string, SeqOneByteString::kHeaderSize));
__ movp(separator_operand, string);
// 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.
// scratch: Separator length.
// separator_operand (rsp[0x10]): Address of first char of separator.
// Copy the separator to the result.
__ movp(string, separator_operand);
__ movl(string_length, scratch);
__ CopyBytes(result_pos, string, string_length, 2);
__ bind(&loop_3_entry);
// Get string = array[index].
__ movp(string, Operand(elements, index, times_pointer_size, 0));
__ SmiToInteger32(string_length,
FieldOperand(string, String::kLengthOffset));
__ leap(string,
FieldOperand(string, SeqOneByteString::kHeaderSize));
__ CopyBytes(result_pos, string, string_length);
__ incq(index);
__ j(not_equal, &loop_3); // Loop while (index < 0).
__ bind(&done);
__ movp(rax, result_operand);
__ bind(&return_result);
// Drop temp values from the stack, and restore context register.
__ addp(rsp, Immediate(3 * kPointerSize));
__ movp(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
context()->Plug(rax);
}
void FullCodeGenerator::EmitDebugIsActive(CallRuntime* expr) {
DCHECK(expr->arguments()->length() == 0);
ExternalReference debug_is_active =
......
......@@ -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