| Index: src/arm/codegen-arm.cc
|
| diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc
|
| index c33c1dacc5c915ad6875885d337877864b9ee441..49396914d73caf7c23ff5053cb711217c00e43c3 100644
|
| --- a/src/arm/codegen-arm.cc
|
| +++ b/src/arm/codegen-arm.cc
|
| @@ -3551,7 +3551,8 @@ void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
|
| Load(args->at(0));
|
| Load(args->at(1));
|
|
|
| - frame_->CallRuntime(Runtime::kStringAdd, 2);
|
| + StringAddStub stub(NO_STRING_ADD_FLAGS);
|
| + frame_->CallStub(&stub, 2);
|
| frame_->EmitPush(r0);
|
| }
|
|
|
| @@ -5332,7 +5333,7 @@ static void HandleBinaryOpSlowCases(MacroAssembler* masm,
|
| // r1 : first argument
|
| // r0 : second argument
|
| // sp[0] : second argument
|
| - // sp[1] : first argument
|
| + // sp[4] : first argument
|
|
|
| Label not_strings, not_string1, string1;
|
| __ tst(r1, Operand(kSmiTagMask));
|
| @@ -5347,7 +5348,8 @@ static void HandleBinaryOpSlowCases(MacroAssembler* masm,
|
| __ b(ge, &string1);
|
|
|
| // First and second argument are strings.
|
| - __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1);
|
| + StringAddStub stub(NO_STRING_CHECK_IN_STUB);
|
| + __ TailCallStub(&stub);
|
|
|
| // Only first argument is a string.
|
| __ bind(&string1);
|
| @@ -5361,7 +5363,6 @@ static void HandleBinaryOpSlowCases(MacroAssembler* masm,
|
| __ b(ge, ¬_strings);
|
|
|
| // Only second argument is a string.
|
| - __ b(¬_strings);
|
| __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_JS);
|
|
|
| __ bind(¬_strings);
|
| @@ -6841,14 +6842,14 @@ void StringStubBase::GenerateCopyCharacters(MacroAssembler* masm,
|
| __ b(eq, &done);
|
|
|
| __ bind(&loop);
|
| + __ ldrb(scratch, MemOperand(src, 1, PostIndex));
|
| + // Perform sub between load and dependent store to get the load time to
|
| + // complete.
|
| __ sub(count, count, Operand(1), SetCC);
|
| - __ ldrb(scratch, MemOperand(src, count), pl);
|
| - // Move branch between load and dependent store to not waste the cycle for
|
| - // each iteration of the loop. It does cost an extra instruction on the
|
| + __ strb(scratch, MemOperand(dest, 1, PostIndex));
|
| // last iteration.
|
| - __ b(mi, &done);
|
| - __ strb(scratch, MemOperand(dest, count));
|
| - __ b(&loop);
|
| + __ b(gt, &loop);
|
| +
|
| __ bind(&done);
|
| }
|
|
|
| @@ -7218,12 +7219,10 @@ void StringCompareStub::Generate(MacroAssembler* masm) {
|
| Label runtime;
|
|
|
| // Stack frame on entry.
|
| - // sp[0]: return address
|
| - // sp[4]: right string
|
| - // sp[8]: left string
|
| -
|
| - __ ldr(r0, MemOperand(sp, 2 * kPointerSize)); // left
|
| - __ ldr(r1, MemOperand(sp, 1 * kPointerSize)); // right
|
| + // sp[0]: right string
|
| + // sp[4]: left string
|
| + __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // left
|
| + __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // right
|
|
|
| Label not_same;
|
| __ cmp(r0, r1);
|
| @@ -7252,6 +7251,220 @@ void StringCompareStub::Generate(MacroAssembler* masm) {
|
| }
|
|
|
|
|
| +void StringAddStub::Generate(MacroAssembler* masm) {
|
| + Label string_add_runtime;
|
| + // Stack on entry:
|
| + // sp[0]: second argument.
|
| + // sp[4]: first argument.
|
| +
|
| + // Load the two arguments.
|
| + __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // First argument.
|
| + __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // Second argument.
|
| +
|
| + // Make sure that both arguments are strings if not known in advance.
|
| + if (string_check_) {
|
| + ASSERT_EQ(0, kSmiTag);
|
| + __ JumpIfEitherSmi(r0, r1, &string_add_runtime);
|
| + // Load instance types.
|
| + __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
|
| + __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
|
| + __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
|
| + __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
|
| + ASSERT_EQ(0, kStringTag);
|
| + // If either is not a string, go to runtime.
|
| + __ tst(r4, Operand(kIsNotStringMask));
|
| + __ tst(r5, Operand(kIsNotStringMask), eq);
|
| + __ b(ne, &string_add_runtime);
|
| + }
|
| +
|
| + // Both arguments are strings.
|
| + // r0: first string
|
| + // r1: second string
|
| + // r4: first string instance type (if string_check_)
|
| + // r5: second string instance type (if string_check_)
|
| + {
|
| + Label strings_not_empty;
|
| + // Check if either of the strings are empty. In that case return the other.
|
| + __ ldr(r2, FieldMemOperand(r0, String::kLengthOffset));
|
| + __ ldr(r3, FieldMemOperand(r1, String::kLengthOffset));
|
| + __ cmp(r2, Operand(0)); // Test if first string is empty.
|
| + __ mov(r0, Operand(r1), LeaveCC, eq); // If first is empty, return second.
|
| + __ cmp(r3, Operand(0), ne); // Else test if second string is empty.
|
| + __ b(ne, &strings_not_empty); // If either string was empty, return r0.
|
| +
|
| + __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
|
| + __ add(sp, sp, Operand(2 * kPointerSize));
|
| + __ Ret();
|
| +
|
| + __ bind(&strings_not_empty);
|
| + }
|
| +
|
| + // Both strings are non-empty.
|
| + // r0: first string
|
| + // r1: second string
|
| + // r2: length of first string
|
| + // r3: length of second string
|
| + // r4: first string instance type (if string_check_)
|
| + // r5: second string instance type (if string_check_)
|
| + // Look at the length of the result of adding the two strings.
|
| + Label string_add_flat_result;
|
| + // Adding two lengths can't overflow.
|
| + ASSERT(String::kMaxLength * 2 > String::kMaxLength);
|
| + __ add(r6, r2, Operand(r3));
|
| + // Use the runtime system when adding two one character strings, as it
|
| + // contains optimizations for this specific case using the symbol table.
|
| + __ cmp(r6, Operand(2));
|
| + __ b(eq, &string_add_runtime);
|
| + // Check if resulting string will be flat.
|
| + __ cmp(r6, Operand(String::kMinNonFlatLength));
|
| + __ b(lt, &string_add_flat_result);
|
| + // Handle exceptionally long strings in the runtime system.
|
| + ASSERT((String::kMaxLength & 0x80000000) == 0);
|
| + ASSERT(IsPowerOf2(String::kMaxLength + 1));
|
| + // kMaxLength + 1 is representable as shifted literal, kMaxLength is not.
|
| + __ cmp(r6, Operand(String::kMaxLength + 1));
|
| + __ b(hs, &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.
|
| + if (!string_check_) {
|
| + __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
|
| + __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
|
| + __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
|
| + __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
|
| + }
|
| + Label non_ascii, allocated;
|
| + ASSERT_EQ(0, kTwoByteStringTag);
|
| + __ tst(r4, Operand(kStringEncodingMask));
|
| + __ tst(r5, Operand(kStringEncodingMask), ne);
|
| + __ b(eq, &non_ascii);
|
| +
|
| + // Allocate an ASCII cons string.
|
| + __ AllocateAsciiConsString(r7, r6, r4, r5, &string_add_runtime);
|
| + __ bind(&allocated);
|
| + // Fill the fields of the cons string.
|
| + __ str(r0, FieldMemOperand(r7, ConsString::kFirstOffset));
|
| + __ str(r1, FieldMemOperand(r7, ConsString::kSecondOffset));
|
| + __ mov(r0, Operand(r7));
|
| + __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
|
| + __ add(sp, sp, Operand(2 * kPointerSize));
|
| + __ Ret();
|
| +
|
| + __ bind(&non_ascii);
|
| + // Allocate a two byte cons string.
|
| + __ AllocateTwoByteConsString(r7, r6, r4, r5, &string_add_runtime);
|
| + __ jmp(&allocated);
|
| +
|
| + // Handle creating a flat result. First check that both strings are
|
| + // sequential and that they have the same encoding.
|
| + // r0: first string
|
| + // r1: second string
|
| + // r2: length of first string
|
| + // r3: length of second string
|
| + // r4: first string instance type (if string_check_)
|
| + // r5: second string instance type (if string_check_)
|
| + // r6: sum of lengths.
|
| + __ bind(&string_add_flat_result);
|
| + if (!string_check_) {
|
| + __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
|
| + __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
|
| + __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
|
| + __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
|
| + }
|
| + // Check that both strings are sequential.
|
| + ASSERT_EQ(0, kSeqStringTag);
|
| + __ tst(r4, Operand(kStringRepresentationMask));
|
| + __ tst(r5, Operand(kStringRepresentationMask), eq);
|
| + __ b(ne, &string_add_runtime);
|
| + // Now check if both strings have the same encoding (ASCII/Two-byte).
|
| + // r0: first string
|
| + // r1: second string
|
| + // r2: length of first string
|
| + // r3: length of second string
|
| + // r6: sum of lengths.
|
| + Label non_ascii_string_add_flat_result;
|
| + ASSERT(IsPowerOf2(kStringEncodingMask)); // Just one bit to test.
|
| + __ eor(r7, r4, Operand(r5));
|
| + __ tst(r7, Operand(kStringEncodingMask));
|
| + __ b(ne, &string_add_runtime);
|
| + // And see if it's ASCII or two-byte.
|
| + __ tst(r4, Operand(kStringEncodingMask));
|
| + __ b(eq, &non_ascii_string_add_flat_result);
|
| +
|
| + // Both strings are sequential ASCII strings. We also know that they are
|
| + // short (since the sum of the lengths is less than kMinNonFlatLength).
|
| + __ AllocateAsciiString(r7, r6, r4, r5, r9, &string_add_runtime);
|
| + // Locate first character of result.
|
| + __ add(r6, r7, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
| + // Locate first character of first argument.
|
| + __ add(r0, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
| + // r0: first character of first string
|
| + // r1: second string
|
| + // r2: length of first string
|
| + // r3: length of second string
|
| + // r6: first character of result
|
| + // r7: result string
|
| + GenerateCopyCharacters(masm, r6, r0, r2, r4, true);
|
| +
|
| + // Load second argument and locate first character.
|
| + __ add(r1, r1, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
| + // r1: first character of second string
|
| + // r3: length of second string
|
| + // r6: next character of result
|
| + // r7: result string
|
| + GenerateCopyCharacters(masm, r6, r1, r3, r4, true);
|
| + __ mov(r0, Operand(r7));
|
| + __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
|
| + __ add(sp, sp, Operand(2 * kPointerSize));
|
| + __ Ret();
|
| +
|
| + __ bind(&non_ascii_string_add_flat_result);
|
| + // Both strings are sequential two byte strings.
|
| + // r0: first character of first string
|
| + // r1: second string
|
| + // r2: length of first string
|
| + // r3: length of second string
|
| + // r6: sum of length of strings.
|
| + __ AllocateTwoByteString(r7, r6, r4, r5, r9, &string_add_runtime);
|
| + // r0: first string
|
| + // r1: second string
|
| + // r2: length of first string
|
| + // r3: length of second string
|
| + // r7: result string
|
| +
|
| + // Locate first character of result.
|
| + __ add(r6, r7, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
| + // Locate first character of first argument.
|
| + __ add(r0, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
| +
|
| + // r0: first character of first string
|
| + // r1: second string
|
| + // r2: length of first string
|
| + // r3: length of second string
|
| + // r6: first character of result
|
| + // r7: result string
|
| + GenerateCopyCharacters(masm, r6, r0, r2, r4, false);
|
| +
|
| + // Locate first character of second argument.
|
| + __ add(r1, r1, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
| +
|
| + // r1: first character of second string
|
| + // r3: length of second string
|
| + // r6: next character of result (after copy of first string)
|
| + // r7: result string
|
| + GenerateCopyCharacters(masm, r6, r1, r3, r4, false);
|
| +
|
| + __ mov(r0, Operand(r7));
|
| + __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
|
| + __ add(sp, sp, Operand(2 * kPointerSize));
|
| + __ Ret();
|
| +
|
| + // Just jump to runtime to add the two strings.
|
| + __ bind(&string_add_runtime);
|
| + __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1);
|
| +}
|
| +
|
| +
|
| #undef __
|
|
|
| } } // namespace v8::internal
|
|
|