| Index: src/x64/codegen-x64.cc
|
| ===================================================================
|
| --- src/x64/codegen-x64.cc (revision 3430)
|
| +++ src/x64/codegen-x64.cc (working copy)
|
| @@ -4051,7 +4051,8 @@
|
| Load(args->at(0));
|
| Load(args->at(1));
|
|
|
| - Result answer = frame_->CallRuntime(Runtime::kStringAdd, 2);
|
| + StringAddStub stub(NO_STRING_ADD_FLAGS);
|
| + Result answer = frame_->CallStub(&stub, 2);
|
| frame_->Push(&answer);
|
| }
|
|
|
| @@ -7796,8 +7797,8 @@
|
| __ j(above_equal, &string1);
|
|
|
| // First and second argument are strings.
|
| - Runtime::Function* f = Runtime::FunctionForId(Runtime::kStringAdd);
|
| - __ TailCallRuntime(ExternalReference(f), 2, f->result_size);
|
| + StringAddStub stub(NO_STRING_CHECK_IN_STUB);
|
| + __ TailCallStub(&stub);
|
|
|
| // Only first argument is a string.
|
| __ bind(&string1);
|
| @@ -7880,6 +7881,234 @@
|
| return (static_cast<unsigned>(cc_) << 1) | (strict_ ? 1 : 0);
|
| }
|
|
|
| +
|
| +void StringAddStub::Generate(MacroAssembler* masm) {
|
| + Label string_add_runtime;
|
| +
|
| + // Load the two arguments.
|
| + __ movq(rax, Operand(rsp, 2 * kPointerSize)); // First argument.
|
| + __ movq(rdx, Operand(rsp, 1 * kPointerSize)); // Second argument.
|
| +
|
| + // Make sure that both arguments are strings if not known in advance.
|
| + if (string_check_) {
|
| + Condition is_smi;
|
| + is_smi = masm->CheckSmi(rax);
|
| + __ j(is_smi, &string_add_runtime);
|
| + __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, r8);
|
| + __ j(above_equal, &string_add_runtime);
|
| +
|
| + // First argument is a a string, test second.
|
| + is_smi = masm->CheckSmi(rdx);
|
| + __ j(is_smi, &string_add_runtime);
|
| + __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, r9);
|
| + __ j(above_equal, &string_add_runtime);
|
| + }
|
| +
|
| + // Both arguments are strings.
|
| + // rax: first string
|
| + // rdx: 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;
|
| + __ movl(rcx, FieldOperand(rdx, String::kLengthOffset));
|
| + __ testl(rcx, rcx);
|
| + __ j(not_zero, &second_not_zero_length);
|
| + // Second string is empty, result is first string which is already in rax.
|
| + __ IncrementCounter(&Counters::string_add_native, 1);
|
| + __ ret(2 * kPointerSize);
|
| + __ bind(&second_not_zero_length);
|
| + __ movl(rbx, FieldOperand(rax, String::kLengthOffset));
|
| + __ testl(rbx, rbx);
|
| + __ j(not_zero, &both_not_zero_length);
|
| + // First string is empty, result is second string which is in rdx.
|
| + __ movq(rax, rdx);
|
| + __ IncrementCounter(&Counters::string_add_native, 1);
|
| + __ ret(2 * kPointerSize);
|
| +
|
| + // Both strings are non-empty.
|
| + // rax: first string
|
| + // rbx: length of first string
|
| + // ecx: length of second string
|
| + // edx: second string
|
| + // r8: instance type of first string if string check was performed above
|
| + // r9: instance type of first string if string check was performed above
|
| + Label string_add_flat_result;
|
| + __ bind(&both_not_zero_length);
|
| + // Look at the length of the result of adding the two strings.
|
| + __ addl(rbx, rcx);
|
| + // Use the runtime system when adding two one character strings, as it
|
| + // contains optimizations for this specific case using the symbol table.
|
| + __ cmpl(rbx, Immediate(2));
|
| + __ j(equal, &string_add_runtime);
|
| + // If arguments where known to be strings, maps are not loaded to r8 and r9
|
| + // by the code above.
|
| + if (!string_check_) {
|
| + __ movq(r8, FieldOperand(rax, HeapObject::kMapOffset));
|
| + __ movq(r9, FieldOperand(rdx, HeapObject::kMapOffset));
|
| + }
|
| + // Get the instance types of the two strings as they will be needed soon.
|
| + __ movzxbl(r8, FieldOperand(r8, Map::kInstanceTypeOffset));
|
| + __ movzxbl(r9, FieldOperand(r9, Map::kInstanceTypeOffset));
|
| + // Check if resulting string will be flat.
|
| + __ cmpl(rbx, Immediate(String::kMinNonFlatLength));
|
| + __ j(below, &string_add_flat_result);
|
| + // Handle exceptionally long strings in the runtime system.
|
| + ASSERT((String::kMaxLength & 0x80000000) == 0);
|
| + __ cmpl(rbx, Immediate(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.
|
| + // rax: first string
|
| + // ebx: length of resulting flat string
|
| + // rdx: second string
|
| + // r8: instance type of first string
|
| + // r9: instance type of second string
|
| + Label non_ascii, allocated;
|
| + __ movl(rcx, r8);
|
| + __ and_(rcx, r9);
|
| + ASSERT(kStringEncodingMask == kAsciiStringTag);
|
| + __ testl(rcx, Immediate(kAsciiStringTag));
|
| + __ j(zero, &non_ascii);
|
| + // Allocate an acsii cons string.
|
| + __ AllocateAsciiConsString(rcx, rdi, no_reg, &string_add_runtime);
|
| + __ bind(&allocated);
|
| + // Fill the fields of the cons string.
|
| + __ movl(FieldOperand(rcx, ConsString::kLengthOffset), rbx);
|
| + __ movl(FieldOperand(rcx, ConsString::kHashFieldOffset),
|
| + Immediate(String::kEmptyHashField));
|
| + __ movq(FieldOperand(rcx, ConsString::kFirstOffset), rax);
|
| + __ movq(FieldOperand(rcx, ConsString::kSecondOffset), rdx);
|
| + __ movq(rax, rcx);
|
| + __ IncrementCounter(&Counters::string_add_native, 1);
|
| + __ ret(2 * kPointerSize);
|
| + __ bind(&non_ascii);
|
| + // Allocate a two byte cons string.
|
| + __ AllocateConsString(rcx, rdi, no_reg, &string_add_runtime);
|
| + __ jmp(&allocated);
|
| +
|
| + // Handle creating a flat result. First check that both strings are not
|
| + // external strings.
|
| + // rax: first string
|
| + // ebx: length of resulting flat string
|
| + // rdx: second string
|
| + // r8: instance type of first string
|
| + // r9: instance type of first string
|
| + __ bind(&string_add_flat_result);
|
| + __ movl(rcx, r8);
|
| + __ and_(rcx, Immediate(kStringRepresentationMask));
|
| + __ cmpl(rcx, Immediate(kExternalStringTag));
|
| + __ j(equal, &string_add_runtime);
|
| + __ movl(rcx, r9);
|
| + __ and_(rcx, Immediate(kStringRepresentationMask));
|
| + __ cmpl(rcx, Immediate(kExternalStringTag));
|
| + __ j(equal, &string_add_runtime);
|
| + // Now check if both strings are ascii strings.
|
| + // rax: first string
|
| + // ebx: length of resulting flat string
|
| + // rdx: second string
|
| + // r8: instance type of first string
|
| + // r9: instance type of second string
|
| + Label non_ascii_string_add_flat_result;
|
| + ASSERT(kStringEncodingMask == kAsciiStringTag);
|
| + __ testl(r8, Immediate(kAsciiStringTag));
|
| + __ j(zero, &non_ascii_string_add_flat_result);
|
| + __ testl(r9, Immediate(kAsciiStringTag));
|
| + __ j(zero, &string_add_runtime);
|
| + // Both strings are ascii strings. As they are short they are both flat.
|
| + __ AllocateAsciiString(rcx, rbx, rdi, r14, r15, &string_add_runtime);
|
| + // rcx: result string
|
| + __ movq(rbx, rcx);
|
| + // Locate first character of result.
|
| + __ addq(rcx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
| + // Locate first character of first argument
|
| + __ movl(rdi, FieldOperand(rax, String::kLengthOffset));
|
| + __ addq(rax, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
| + // rax: first char of first argument
|
| + // rbx: result string
|
| + // rcx: first character of result
|
| + // rdx: second string
|
| + // rdi: length of first argument
|
| + GenerateCopyCharacters(masm, rcx, rax, rdi, true);
|
| + // Locate first character of second argument.
|
| + __ movl(rdi, FieldOperand(rdx, String::kLengthOffset));
|
| + __ addq(rdx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
| + // rbx: result string
|
| + // rcx: next character of result
|
| + // rdx: first char of second argument
|
| + // rdi: length of second argument
|
| + GenerateCopyCharacters(masm, rcx, rdx, rdi, true);
|
| + __ movq(rax, rbx);
|
| + __ IncrementCounter(&Counters::string_add_native, 1);
|
| + __ ret(2 * kPointerSize);
|
| +
|
| + // Handle creating a flat two byte result.
|
| + // rax: first string - known to be two byte
|
| + // rbx: length of resulting flat string
|
| + // rdx: second string
|
| + // r8: instance type of first string
|
| + // r9: instance type of first string
|
| + __ bind(&non_ascii_string_add_flat_result);
|
| + __ and_(r9, Immediate(kAsciiStringTag));
|
| + __ j(not_zero, &string_add_runtime);
|
| + // Both strings are two byte strings. As they are short they are both
|
| + // flat.
|
| + __ AllocateTwoByteString(rcx, rbx, rdi, r14, r15, &string_add_runtime);
|
| + // rcx: result string
|
| + __ movq(rbx, rcx);
|
| + // Locate first character of result.
|
| + __ addq(rcx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
| + // Locate first character of first argument.
|
| + __ movl(rdi, FieldOperand(rax, String::kLengthOffset));
|
| + __ addq(rax, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
| + // rax: first char of first argument
|
| + // rbx: result string
|
| + // rcx: first character of result
|
| + // rdx: second argument
|
| + // rdi: length of first argument
|
| + GenerateCopyCharacters(masm, rcx, rax, rdi, false);
|
| + // Locate first character of second argument.
|
| + __ movl(rdi, FieldOperand(rdx, String::kLengthOffset));
|
| + __ addq(rdx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
| + // rbx: result string
|
| + // rcx: next character of result
|
| + // rdx: first char of second argument
|
| + // rdi: length of second argument
|
| + GenerateCopyCharacters(masm, rcx, rdx, rdi, false);
|
| + __ movq(rax, rbx);
|
| + __ 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,
|
| + 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) {
|
| + __ movb(kScratchRegister, Operand(src, 0));
|
| + __ movb(Operand(dest, 0), kScratchRegister);
|
| + __ addq(src, Immediate(1));
|
| + __ addq(dest, Immediate(1));
|
| + } else {
|
| + __ movzxwl(kScratchRegister, Operand(src, 0));
|
| + __ movw(Operand(dest, 0), kScratchRegister);
|
| + __ addq(src, Immediate(2));
|
| + __ addq(dest, Immediate(2));
|
| + }
|
| + __ subl(count, Immediate(1));
|
| + __ j(not_zero, &loop);
|
| +}
|
| +
|
| +
|
| #undef __
|
|
|
| #define __ masm.
|
|
|