[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} Review URL: https://codereview.chromium.org/1708523002 Cr-Commit-Position: refs/heads/master@{#34146}
This commit is contained in:
parent
be23438170
commit
3b1211ac2f
@ -97,8 +97,6 @@ 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") \
|
||||
@ -129,8 +127,6 @@ 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") \
|
||||
|
@ -12875,16 +12875,6 @@ 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,7 +2213,6 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
|
||||
F(MathPow) \
|
||||
F(HasCachedArrayIndex) \
|
||||
F(GetCachedArrayIndex) \
|
||||
F(FastOneByteArrayJoin) \
|
||||
F(DebugBreakInOptimizedCode) \
|
||||
F(StringCharCodeAt) \
|
||||
F(SubString) \
|
||||
|
@ -3416,243 +3416,6 @@ 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, ¬_size_one_array);
|
||||
__ ldr(r0, FieldMemOperand(elements, FixedArray::kHeaderSize));
|
||||
__ b(&done);
|
||||
|
||||
__ bind(¬_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 =
|
||||
|
@ -3223,226 +3223,6 @@ 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, ¬_size_one_array);
|
||||
__ Ldr(result, FieldMemOperand(elements, FixedArray::kHeaderSize));
|
||||
__ B(&done);
|
||||
|
||||
__ Bind(¬_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,7 +535,6 @@ class FullCodeGenerator: public AstVisitor {
|
||||
F(HasCachedArrayIndex) \
|
||||
F(GetCachedArrayIndex) \
|
||||
F(GetSuperConstructor) \
|
||||
F(FastOneByteArrayJoin) \
|
||||
F(GeneratorNext) \
|
||||
F(GeneratorReturn) \
|
||||
F(GeneratorThrow) \
|
||||
|
@ -3306,275 +3306,6 @@ 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, ¬_size_one_array);
|
||||
__ mov(scratch, FieldOperand(elements, FixedArray::kHeaderSize));
|
||||
__ mov(result_operand, scratch);
|
||||
__ jmp(&done);
|
||||
|
||||
__ bind(¬_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 =
|
||||
|
@ -3418,238 +3418,6 @@ 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(¬_size_one_array, ne, array_length, Operand(1));
|
||||
__ lw(v0, FieldMemOperand(elements, FixedArray::kHeaderSize));
|
||||
__ Branch(&done);
|
||||
|
||||
__ bind(¬_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 =
|
||||
|
@ -3424,240 +3424,6 @@ 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(¬_size_one_array, ne, array_length, Operand(1));
|
||||
__ ld(v0, FieldMemOperand(elements, FixedArray::kHeaderSize));
|
||||
__ Branch(&done);
|
||||
|
||||
__ bind(¬_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 =
|
||||
|
@ -3397,261 +3397,6 @@ 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(¬_size_one_array);
|
||||
__ LoadP(r3, FieldMemOperand(elements, FixedArray::kHeaderSize));
|
||||
__ b(&done);
|
||||
|
||||
__ bind(¬_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 =
|
||||
|
@ -3289,296 +3289,6 @@ 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, ¬_size_one_array);
|
||||
__ movp(rax, FieldOperand(elements, FixedArray::kHeaderSize));
|
||||
__ jmp(&return_result);
|
||||
|
||||
__ bind(¬_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 =
|
||||
|
@ -3298,275 +3298,6 @@ 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, ¬_size_one_array);
|
||||
__ mov(scratch, FieldOperand(elements, FixedArray::kHeaderSize));
|
||||
__ mov(result_operand, scratch);
|
||||
__ jmp(&done);
|
||||
|
||||
__ bind(¬_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,8 +209,6 @@ 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.
|
||||
@ -233,9 +231,6 @@ 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
|
||||
@ -447,9 +442,6 @@ 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,15 +489,6 @@ 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,7 +51,6 @@ namespace internal {
|
||||
F(GetCachedArrayIndex, 1, 1) \
|
||||
F(FixedArrayGet, 2, 1) \
|
||||
F(FixedArraySet, 3, 1) \
|
||||
F(FastOneByteArrayJoin, 2, 1) \
|
||||
F(ArraySpeciesConstructor, 1, 1)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user