Chromium Code Reviews| Index: src/ia32/full-codegen-ia32.cc |
| =================================================================== |
| --- src/ia32/full-codegen-ia32.cc (revision 6283) |
| +++ src/ia32/full-codegen-ia32.cc (working copy) |
| @@ -3351,8 +3351,12 @@ |
| void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { |
| - Label bailout; |
| - Label done; |
| + Label bailout, done, one_char_separator, long_separator, |
| + non_trivial_array, not_size_one_array, loop, loop_condition, |
| + loop_1, loop_1_condition, loop_1a, loop_1a_condition, |
| + loop_2, loop_2_entry, loop_2a, loop_2a_condition, |
| + loop_3, loop_3_entry, loop_3a, loop_3a_condition, |
| + loop_3b, loop_3b_condition; |
|
Lasse Reichstein
2011/01/13 09:20:32
That's a lot of loops. Could they be given more te
William Hesse
2011/01/13 16:25:59
Loops eliminated by making the string copy a macro
|
| ASSERT(args->length() == 2); |
| // We will leave the separator on the stack until the end of the function. |
| @@ -3362,28 +3366,28 @@ |
| // All aliases of the same register have disjoint lifetimes. |
| Register array = eax; |
| - Register result_pos = no_reg; |
| + Register elements = no_reg; // Will be eax. |
| + Register result_pos = no_reg; // Will be eax. |
| Register index = edi; |
| - Register current_string_length = ecx; // Will be ecx when live. |
| + Register current_string_length = ecx; |
| Register current_string = edx; |
| Register scratch = ebx; |
| - Register scratch_2 = esi; |
| - Register new_padding_chars = scratch_2; |
| + Register array_length = esi; |
| + Register pos = no_reg; // Will be esi. |
| - Operand separator = Operand(esp, 4 * kPointerSize); // Already pushed. |
| - Operand elements = Operand(esp, 3 * kPointerSize); |
| - Operand result = Operand(esp, 2 * kPointerSize); |
| - Operand padding_chars = Operand(esp, 1 * kPointerSize); |
| - Operand array_length = Operand(esp, 0); |
| - __ sub(Operand(esp), Immediate(4 * kPointerSize)); |
| + // Separator operand is already pushed. |
| + Operand separator_operand = Operand(esp, 3 * kPointerSize); |
| + Operand elements_operand = Operand(esp, 2 * kPointerSize); |
| + Operand result_operand = Operand(esp, 1 * kPointerSize); |
| + Operand array_length_operand = Operand(esp, 0); |
| + __ sub(Operand(esp), Immediate(3 * kPointerSize)); |
| - |
| - // Check that eax is a JSArray |
| + // Check that the array is a JSArray |
| __ test(array, Immediate(kSmiTagMask)); |
| __ j(zero, &bailout); |
| __ CmpObjectType(array, JS_ARRAY_TYPE, scratch); |
| @@ -3394,27 +3398,42 @@ |
| 1 << Map::kHasFastElements); |
|
Lasse Reichstein
2011/01/13 09:20:32
Will Copy-on-Write arrays have fast elements if qu
William Hesse
2011/01/13 16:25:59
Yes, they will (I checked).
|
| __ j(zero, &bailout); |
| - // If the array is empty, return the empty string. |
| + // If the array has length zero, return the empty string. |
| __ mov(scratch, FieldOperand(array, JSArray::kLengthOffset)); |
| __ sar(scratch, 1); |
| - Label non_trivial; |
| - __ j(not_zero, &non_trivial); |
| - __ mov(result, Factory::empty_string()); |
| + __ j(not_zero, &non_trivial_array); |
| + __ mov(result_operand, Factory::empty_string()); |
| __ jmp(&done); |
| - __ bind(&non_trivial); |
| + // Save the array length. |
| + __ bind(&non_trivial_array); |
| + __ mov(array_length_operand, scratch); |
| __ mov(array_length, scratch); |
| + // Save the FixedArray containing array's elements. |
| __ mov(scratch, FieldOperand(array, JSArray::kElementsOffset)); |
| - __ mov(elements, scratch); |
| - |
| // End of array's live range. |
| - result_pos = array; |
| + elements = array; |
| array = no_reg; |
| + __ mov(elements_operand, scratch); |
| + __ mov(elements, scratch); |
| - // Check that the separator is a flat ascii string. |
| - __ mov(current_string, separator); |
| + // Check that all array elements are sequential ascii strings, and |
|
Lasse Reichstein
2011/01/13 09:20:32
ascii -> ASCII.
William Hesse
2011/01/13 16:25:59
Both forms occur in our comments, about 1/3 lowerc
|
| + // accumulate the sum of their lengths. |
|
Lasse Reichstein
2011/01/13 09:20:32
Say that the sum is maintained as a Smi value.
William Hesse
2011/01/13 16:25:59
Done.
|
| + __ Set(index, Immediate(0)); |
| + __ Set(current_string_length, Immediate(0)); |
| + // Loop condition: while (index < length). |
| + // Live loop registers: index, array_length, current_string, |
| + // scratch, current_string_length, elements. |
| + __ jmp(&loop_condition); |
| + __ bind(&loop); |
| + __ cmp(index, Operand(array_length)); |
| + __ j(greater_equal, &done); |
| + |
| + __ mov(current_string, FieldOperand(elements, index, |
| + times_pointer_size, |
| + FixedArray::kHeaderSize)); |
| __ test(current_string, Immediate(kSmiTagMask)); |
| __ j(zero, &bailout); |
| __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset)); |
| @@ -3423,18 +3442,35 @@ |
| kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); |
| __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag); |
| __ j(not_equal, &bailout); |
| - // If the separator is the empty string, replace it with NULL. |
| - // The test for NULL is quicker than the empty string test, in a loop. |
| - __ cmp(FieldOperand(current_string, SeqAsciiString::kLengthOffset), |
| - Immediate(0)); |
| - Label separator_checked; |
| - __ j(not_zero, &separator_checked); |
| - __ mov(separator, Immediate(0)); |
| - __ bind(&separator_checked); |
| + __ add(current_string_length, |
| + FieldOperand(current_string, SeqAsciiString::kLengthOffset)); |
| + __ j(overflow, &bailout); |
| + __ add(Operand(index), Immediate(1)); |
| + __ bind(&loop_condition); |
| + __ cmp(index, Operand(array_length)); |
| + __ j(less, &loop); |
| - // Check that elements[0] is a flat ascii string, and copy it in new space. |
| - __ mov(scratch, elements); |
| - __ mov(current_string, FieldOperand(scratch, FixedArray::kHeaderSize)); |
| + // 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 elements live range |
| + result_pos = elements; |
| + elements = no_reg; |
| + |
| + // End of array_length live range. |
| + pos = array_length; |
| + array_length = no_reg; |
| + |
| + // Live register: |
| + // current_string_length: Sum of string lengths, as a smi. |
| + |
| + // Check that the separator is a flat ascii string. |
|
Lasse Reichstein
2011/01/13 09:20:32
ASCII.
William Hesse
2011/01/13 16:25:59
Done.
|
| + __ mov(current_string, separator_operand); |
| __ test(current_string, Immediate(kSmiTagMask)); |
| __ j(zero, &bailout); |
| __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset)); |
| @@ -3444,90 +3480,224 @@ |
| __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag); |
| __ j(not_equal, &bailout); |
| - // Allocate space to copy it. Round up the size to the alignment granularity. |
| - __ mov(current_string_length, |
| - FieldOperand(current_string, String::kLengthOffset)); |
| + // Add (separator length times array_length) - separator length |
| + // to current_string_length. |
| + __ mov(scratch, separator_operand); |
| + __ mov(scratch, FieldOperand(scratch, SeqAsciiString::kLengthOffset)); |
| + __ sub(current_string_length, Operand(scratch)); |
|
Lasse Reichstein
2011/01/13 09:20:32
Perhaps worth noting that this can (and may) give
William Hesse
2011/01/13 16:25:59
Done.
|
| + __ imul(scratch, array_length_operand); |
| + __ j(overflow, &bailout); |
| + __ add(current_string_length, Operand(scratch)); |
| + __ j(overflow, &bailout); |
| + |
| __ shr(current_string_length, 1); |
| - |
| // Live registers and stack values: |
| // current_string_length: length of elements[0]. |
| // New string result in new space = elements[0] |
| - __ AllocateAsciiString(result_pos, current_string_length, scratch_2, |
| - index, no_reg, &bailout); |
| - __ mov(result, result_pos); |
| + __ AllocateAsciiString(result_pos, current_string_length, pos, |
| + index, current_string, &bailout); |
| + __ mov(result_operand, result_pos); |
| + __ lea(result_pos, FieldOperand(result_pos, SeqAsciiString::kHeaderSize)); |
| - // Adjust current_string_length to include padding bytes at end of string. |
| - // Keep track of the number of padding bytes. |
| - __ mov(new_padding_chars, current_string_length); |
| - __ add(Operand(current_string_length), Immediate(kObjectAlignmentMask)); |
| - __ and_(Operand(current_string_length), Immediate(~kObjectAlignmentMask)); |
| - __ sub(new_padding_chars, Operand(current_string_length)); |
| - __ neg(new_padding_chars); |
| - __ mov(padding_chars, new_padding_chars); |
| - Label copy_loop_1_done; |
| - Label copy_loop_1; |
| - __ test(current_string_length, Operand(current_string_length)); |
| - __ j(zero, ©_loop_1_done); |
| - __ bind(©_loop_1); |
| - __ sub(Operand(current_string_length), Immediate(kPointerSize)); |
| - __ mov(scratch, FieldOperand(current_string, current_string_length, |
| - times_1, SeqAsciiString::kHeaderSize)); |
| - __ mov(FieldOperand(result_pos, current_string_length, |
| - times_1, SeqAsciiString::kHeaderSize), |
| - scratch); |
| - __ j(not_zero, ©_loop_1); |
| - __ bind(©_loop_1_done); |
| + __ mov(current_string, separator_operand); |
| + __ cmp(FieldOperand(current_string, SeqAsciiString::kLengthOffset), |
| + Immediate(Smi::FromInt(1))); |
| + __ j(equal, &one_char_separator); |
| + __ j(greater, &long_separator); |
| - __ mov(index, Immediate(1)); |
| + |
| + // Empty separator case |
| + __ mov(index, Immediate(0)); |
| + __ jmp(&loop_1_condition); |
| // Loop condition: while (index < length). |
| - Label loop; |
| - __ bind(&loop); |
| - __ cmp(index, array_length); |
| - __ j(greater_equal, &done); |
| + __ 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. |
| - // If the separator is the empty string, signalled by NULL, skip it. |
| - Label separator_done; |
| - __ mov(current_string, separator); |
| - __ test(current_string, Operand(current_string)); |
| - __ j(zero, &separator_done); |
| + // Get current_string = array[index]. |
| + __ mov(scratch, elements_operand); |
| + __ mov(current_string, FieldOperand(scratch, index, |
| + times_pointer_size, |
| + FixedArray::kHeaderSize)); |
| + __ mov(current_string_length, |
| + FieldOperand(current_string, String::kLengthOffset)); |
| + __ shr(current_string_length, 1); |
| + __ lea(current_string, |
| + FieldOperand(current_string, SeqAsciiString::kHeaderSize)); |
| + __ Set(pos, Immediate(0)); |
| + __ jmp(&loop_1a_condition); |
| - // Append separator to result. It is known to be a flat ascii string. |
| - __ AppendStringToTopOfNewSpace(current_string, current_string_length, |
| - result_pos, scratch, scratch_2, result, |
| - padding_chars, &bailout); |
| - __ bind(&separator_done); |
| + __ bind(&loop_1a); |
| + // Live registers: |
| + // current_string: the start of the current string's data. |
| + // pos: the index into the currently copied character of current_string. |
| + // current_string_length: the length of the current string. |
| + // scratch: the byte being copied. |
| + // result_pos: the position to copy the current string to. |
| + // index: which element of the array is being copied (live across loop). |
| + __ mov_b(scratch, Operand(current_string, pos, times_1, 0)); |
| + __ mov_b(Operand(result_pos, pos, times_1, 0), scratch); |
| + __ inc(pos); |
| + __ bind(&loop_1a_condition); |
| + __ cmp(pos, Operand(current_string_length)); |
| + __ j(less, &loop_1a); |
| - // Add next element of array to the end of the result. |
| + __ add(result_pos, Operand(current_string_length)); |
| + __ add(Operand(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 ascii character value. |
| + __ mov_b(scratch, FieldOperand(current_string, SeqAsciiString::kHeaderSize)); |
| + __ mov_b(separator_operand, scratch); |
| + |
| + __ Set(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 current_string = array[index]. |
| - __ mov(scratch, elements); |
| + __ mov(scratch, elements_operand); |
| __ mov(current_string, FieldOperand(scratch, index, |
| times_pointer_size, |
| FixedArray::kHeaderSize)); |
| - // If current != flat ascii string drop result, return undefined. |
| - __ test(current_string, Immediate(kSmiTagMask)); |
| - __ j(zero, &bailout); |
| - __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset)); |
| - __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); |
| - __ and_(scratch, Immediate( |
| - kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); |
| - __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag); |
| - __ j(not_equal, &bailout); |
| + __ mov(current_string_length, |
| + FieldOperand(current_string, String::kLengthOffset)); |
| + __ shr(current_string_length, 1); |
| + __ lea(current_string, |
| + FieldOperand(current_string, SeqAsciiString::kHeaderSize)); |
| + __ Set(pos, Immediate(0)); |
| + __ jmp(&loop_2a_condition); |
| - // Append current to the result. |
| - __ AppendStringToTopOfNewSpace(current_string, current_string_length, |
| - result_pos, scratch, scratch_2, result, |
| - padding_chars, &bailout); |
| + __ bind(&loop_2a); |
| + // Live registers: |
| + // current_string: the start of the current string's data. |
| + // pos: the index into the currently copied character of current_string. |
| + // current_string_length: the length of the current string. |
| + // scratch: the byte being copied. |
| + // result_pos: the position to copy the current string to. |
| + // index: which element of the array is being copied (live across loop). |
| + __ mov_b(scratch, Operand(current_string, pos, times_1, 0)); |
| + __ mov_b(Operand(result_pos, pos, times_1, 0), scratch); |
| + __ inc(pos); |
| + __ bind(&loop_2a_condition); |
| + __ cmp(pos, Operand(current_string_length)); |
| + __ j(less, &loop_2a); |
| + |
| + __ add(result_pos, Operand(current_string_length)); |
| __ add(Operand(index), Immediate(1)); |
| - __ jmp(&loop); // End while (index < length). |
| + __ 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); |
| + |
| + __ Set(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(current_string, separator_operand); |
| + __ mov(current_string_length, |
| + FieldOperand(current_string, String::kLengthOffset)); |
| + __ shr(current_string_length, 1); |
| + __ lea(current_string, |
| + FieldOperand(current_string, SeqAsciiString::kHeaderSize)); |
| + __ Set(pos, Immediate(0)); |
| + __ jmp(&loop_3a_condition); |
| + |
| + __ bind(&loop_3a); |
| + // Live registers: |
| + // current_string: the start of the separator's data. |
| + // pos: the index into the currently copied character of separator. |
| + // current_string_length: the length of the separator. |
| + // scratch: the byte being copied. |
| + // result_pos: the position to copy the separator to. |
| + // index: which element of the array is being copied (live across loop). |
| + __ mov_b(scratch, Operand(current_string, pos, times_1, 0)); |
| + __ mov_b(Operand(result_pos, pos, times_1, 0), scratch); |
| + __ inc(pos); |
| + __ bind(&loop_3a_condition); |
| + __ cmp(pos, Operand(current_string_length)); |
| + __ j(less, &loop_3a); |
| + |
| + __ add(result_pos, Operand(current_string_length)); |
| + |
| + __ bind(&loop_3_entry); |
| + // Get current_string = array[index]. |
| + __ mov(scratch, elements_operand); |
| + __ mov(current_string, FieldOperand(scratch, index, |
| + times_pointer_size, |
| + FixedArray::kHeaderSize)); |
| + __ mov(current_string_length, |
| + FieldOperand(current_string, String::kLengthOffset)); |
| + __ shr(current_string_length, 1); |
| + __ lea(current_string, |
| + FieldOperand(current_string, SeqAsciiString::kHeaderSize)); |
| + __ Set(pos, Immediate(0)); |
| + __ jmp(&loop_3b_condition); |
| + |
| + __ bind(&loop_3b); |
| + // Live registers: |
| + // current_string: the start of the current string's data. |
| + // pos: the index into the currently copied character of current_string. |
| + // current_string_length: the length of the current string. |
| + // scratch: the byte being copied. |
| + // result_pos: the position to copy the current string to. |
| + // index: which element of the array is being copied (live across loop). |
| + __ mov_b(scratch, Operand(current_string, pos, times_1, 0)); |
| + __ mov_b(Operand(result_pos, pos, times_1, 0), scratch); |
| + __ inc(pos); |
| + __ bind(&loop_3b_condition); |
| + __ cmp(pos, Operand(current_string_length)); |
| + __ j(less, &loop_3b); |
| + |
| + __ add(result_pos, Operand(current_string_length)); |
| + __ add(Operand(index), Immediate(1)); |
| + |
| + __ cmp(index, array_length_operand); |
| + __ j(less, &loop_3); // End while (index < length). |
| + __ jmp(&done); |
| + |
| + |
| __ bind(&bailout); |
| - __ mov(result, Factory::undefined_value()); |
| + __ mov(result_operand, Factory::undefined_value()); |
| __ bind(&done); |
| - __ mov(eax, result); |
| + __ mov(eax, result_operand); |
| // Drop temp values from the stack, and restore context register. |
| - __ add(Operand(esp), Immediate(5 * kPointerSize)); |
| + __ add(Operand(esp), Immediate(4 * kPointerSize)); |
| __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); |
| context()->Plug(eax); |