Chromium Code Reviews| Index: src/ia32/codegen-ia32.cc |
| =================================================================== |
| --- src/ia32/codegen-ia32.cc (revision 3397) |
| +++ src/ia32/codegen-ia32.cc (working copy) |
| @@ -5212,6 +5212,18 @@ |
| } |
| +void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) { |
| + ASSERT_EQ(2, args->length()); |
| + |
| + Load(args->at(0)); |
| + Load(args->at(1)); |
| + |
| + StringAddStub stub(NO_STRING_ADD_FLAGS); |
| + Result answer = frame_->CallStub(&stub, 2); |
| + frame_->Push(&answer); |
| +} |
| + |
| + |
| void CodeGenerator::VisitCallRuntime(CallRuntime* node) { |
| if (CheckForInlineRuntimeCall(node)) { |
| return; |
| @@ -7044,8 +7056,9 @@ |
| __ CmpObjectType(edx, FIRST_NONSTRING_TYPE, edx); |
| __ j(above_equal, &string1); |
| - // First and second argument are strings. |
| - __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1); |
| + // First and second argument are strings. Jump to the string add stub. |
| + StringAddStub stub(NO_STRING_CHECK_IN_STUB); |
| + __ TailCallStub(&stub); |
| // Only first argument is a string. |
| __ bind(&string1); |
| @@ -8172,6 +8185,224 @@ |
| return (static_cast<unsigned>(cc_) << 1) | (strict_ ? 1 : 0); |
| } |
| + |
| +void StringAddStub::Generate(MacroAssembler* masm) { |
| + Label string_add_runtime; |
| + |
| + // Load the two arguments. |
| + __ mov(eax, Operand(esp, 2 * kPointerSize)); // First argument. |
| + __ mov(edx, Operand(esp, 1 * kPointerSize)); // Second argument. |
| + |
| + // Make sure that both arguments are strings if not known in advance. |
| + if (string_check_) { |
| + __ test(eax, Immediate(kSmiTagMask)); |
| + __ j(zero, &string_add_runtime); |
| + __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ebx); |
| + __ j(above_equal, &string_add_runtime); |
| + |
| + // First argument is a a string, test second. |
| + __ test(edx, Immediate(kSmiTagMask)); |
| + __ j(zero, &string_add_runtime); |
| + __ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ebx); |
| + __ j(above_equal, &string_add_runtime); |
| + } |
| + |
| + // Both arguments are strings. |
| + // eax: first string |
| + // edx: second string |
| + // Check if either of the strings are empty. In that case return the other. |
| + Label second_not_zero_length, both_not_zero_length; |
| + __ mov(ecx, FieldOperand(edx, String::kLengthOffset)); |
| + __ test(ecx, Operand(ecx)); |
| + __ j(not_zero, &second_not_zero_length); |
| + // Second string is empty, result is first string which is already in eax. |
| + __ IncrementCounter(&Counters::string_add_native, 1); |
| + __ ret(2 * kPointerSize); |
| + __ bind(&second_not_zero_length); |
| + __ mov(ebx, FieldOperand(eax, String::kLengthOffset)); |
| + __ test(ebx, Operand(ebx)); |
| + __ j(not_zero, &both_not_zero_length); |
| + // First string is empty, result is second string which is in edx. |
| + __ mov(eax, edx); |
| + __ IncrementCounter(&Counters::string_add_native, 1); |
| + __ ret(2 * kPointerSize); |
| + |
| + // Both strings are non-empty. |
| + // eax: first string |
| + // ebx: length of first string |
| + // ecx: length of second string |
| + // edx: second string |
| + // Look at the length of the result of adding the two strings. |
| + Label string_add_flat_result; |
| + __ bind(&both_not_zero_length); |
| + __ add(ebx, Operand(ecx)); |
| + // Use the runtime system when adding two one character strings, as it |
| + // contains optimizations for this specific case using the symbol table. |
| + __ cmp(ebx, 2); |
| + __ j(equal, &string_add_runtime); |
| + // Check if resulting string will be flat. |
| + __ cmp(ebx, String::kMinNonFlatLength); |
| + __ j(below, &string_add_flat_result); |
| + // Handle exceptionally long strings in the runtime system. |
| + ASSERT((String::kMaxLength & 0x80000000) == 0); |
| + __ cmp(ebx, String::kMaxLength); |
| + __ j(above, &string_add_runtime); |
| + |
| + // If result is not supposed to be flat allocate a cons string object. If both |
| + // strings are ascii the result is an ascii cons string. |
| + Label non_ascii, allocated; |
| + __ mov(edi, FieldOperand(eax, HeapObject::kMapOffset)); |
| + __ movzx_b(ecx, FieldOperand(edi, Map::kInstanceTypeOffset)); |
| + __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset)); |
| + __ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset)); |
| + __ and_(ecx, Operand(edi)); |
| + __ test(ecx, Immediate(kAsciiStringTag)); |
| + __ j(zero, &non_ascii); |
| + // Allocate an acsii cons string. |
| + __ AllocateAsciiConsString(ecx, edi, no_reg, &string_add_runtime); |
| + __ bind(&allocated); |
| + // Fill the fields of the cons string. |
| + __ mov(FieldOperand(ecx, ConsString::kLengthOffset), ebx); |
| + __ mov(FieldOperand(ecx, ConsString::kHashFieldOffset), |
| + Immediate(String::kEmptyHashField)); |
| + __ mov(FieldOperand(ecx, ConsString::kFirstOffset), eax); |
| + __ mov(FieldOperand(ecx, ConsString::kSecondOffset), edx); |
| + __ mov(eax, ecx); |
| + __ IncrementCounter(&Counters::string_add_native, 1); |
| + __ ret(2 * kPointerSize); |
| + __ bind(&non_ascii); |
| + // Allocate a two byte cons string. |
| + __ AllocateConsString(ecx, edi, no_reg, &string_add_runtime); |
| + __ jmp(&allocated); |
| + |
| + // Handle creating a flat result. First check that both strings are not |
| + // external strings. |
| + // eax: first string |
| + // ebx: length of resulting flat string |
| + // edx: second string |
| + __ bind(&string_add_flat_result); |
| + __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); |
| + __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); |
| + __ and_(ecx, kStringRepresentationMask); |
| + __ cmp(ecx, kExternalStringTag); |
| + __ j(equal, &string_add_runtime); |
| + __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); |
| + __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); |
| + __ and_(ecx, kStringRepresentationMask); |
| + __ cmp(ecx, kExternalStringTag); |
| + __ j(equal, &string_add_runtime); |
| + // Now check if both strings are ascii strings. |
| + // eax: first string |
| + // ebx: length of resulting flat string |
| + // edx: second string |
| + Label non_ascii_string_add_flat_result; |
| + __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); |
| + __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); |
| + ASSERT(kAsciiStringTag != 0); |
| + __ test(ecx, Immediate(kAsciiStringTag)); |
| + __ j(zero, &non_ascii_string_add_flat_result); |
| + __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); |
| + __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); |
|
Erik Corry
2009/12/03 08:13:56
Here you load the type fields again that you just
|
| + __ test(ecx, Immediate(kAsciiStringTag)); |
| + __ j(zero, &string_add_runtime); |
| + // Both strings are ascii strings. As they are short they are both flat. |
| + __ AllocateAsciiString(eax, ebx, ecx, edx, edi, &string_add_runtime); |
| + // eax: result string |
| + __ mov(ecx, eax); |
| + // Locate first character of result. |
| + __ add(Operand(ecx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
| + // Load first argument and locate first character. |
| + __ mov(edx, Operand(esp, 2 * kPointerSize)); |
| + __ mov(edi, FieldOperand(edx, String::kLengthOffset)); |
| + __ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
| + // eax: result string |
| + // ecx: first character of result |
| + // edx: first char of first argument |
| + // edi: length of first argument |
| + GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true); |
| + // Load second argument and locate first character. |
| + __ mov(edx, Operand(esp, 1 * kPointerSize)); |
| + __ mov(edi, FieldOperand(edx, String::kLengthOffset)); |
| + __ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
| + // eax: result string |
| + // ecx: next character of result |
| + // edx: first char of second argument |
| + // edi: length of second argument |
| + GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true); |
| + __ IncrementCounter(&Counters::string_add_native, 1); |
| + __ ret(2 * kPointerSize); |
| + |
| + // Handle creating a flat two byte result. |
| + // eax: first string - known to be two byte |
| + // ebx: length of resulting flat string |
| + // edx: second string |
| + __ bind(&non_ascii_string_add_flat_result); |
| + __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); |
| + __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); |
|
Erik Corry
2009/12/03 08:13:56
Same issue here.
|
| + __ and_(ecx, kAsciiStringTag); |
| + __ j(not_zero, &string_add_runtime); |
| + // Both strings are two byte strings. As they are short they are both |
| + // flat. |
| + __ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &string_add_runtime); |
| + // eax: result string |
| + __ mov(ecx, eax); |
| + // Locate first character of result. |
| + __ add(Operand(ecx), |
| + Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); |
| + // Load first argument and locate first character. |
| + __ mov(edx, Operand(esp, 2 * kPointerSize)); |
| + __ mov(edi, FieldOperand(edx, String::kLengthOffset)); |
| + __ add(Operand(edx), |
| + Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); |
| + // eax: result string |
| + // ecx: first character of result |
| + // edx: first char of first argument |
| + // edi: length of first argument |
| + GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false); |
| + // Load second argument and locate first character. |
| + __ mov(edx, Operand(esp, 1 * kPointerSize)); |
| + __ mov(edi, FieldOperand(edx, String::kLengthOffset)); |
| + __ add(Operand(edx), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); |
| + // eax: result string |
| + // ecx: next character of result |
| + // edx: first char of second argument |
| + // edi: length of second argument |
| + GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false); |
| + __ IncrementCounter(&Counters::string_add_native, 1); |
| + __ ret(2 * kPointerSize); |
| + |
| + // Just jump to runtime to add the two strings. |
| + __ bind(&string_add_runtime); |
| + __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1); |
| +} |
| + |
| + |
| +void StringAddStub::GenerateCopyCharacters(MacroAssembler* masm, |
| + Register dest, |
| + Register src, |
| + Register count, |
| + Register scratch, |
| + bool ascii) { |
| + Label loop; |
| + __ bind(&loop); |
| + // This loop just copies one character at a time, as it is only used for very |
| + // short strings. |
| + if (ascii) { |
| + __ mov_b(scratch, Operand(src, 0)); |
| + __ mov_b(Operand(dest, 0), scratch); |
| + __ add(Operand(src), Immediate(1)); |
| + __ add(Operand(dest), Immediate(1)); |
| + } else { |
| + __ mov_w(scratch, Operand(src, 0)); |
| + __ mov_w(Operand(dest, 0), scratch); |
| + __ add(Operand(src), Immediate(2)); |
| + __ add(Operand(dest), Immediate(2)); |
| + } |
| + __ sub(Operand(count), Immediate(1)); |
| + __ j(not_zero, &loop); |
| +} |
| + |
| + |
| #undef __ |
| } } // namespace v8::internal |