From dd85270967695935639aee6df7263e52519d159a Mon Sep 17 00:00:00 2001 From: "lrn@chromium.org" Date: Fri, 5 Feb 2010 12:00:42 +0000 Subject: [PATCH] ARM native string addition. Review URL: http://codereview.chromium.org/571005 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3806 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/codegen-arm.cc | 245 ++++++++++++++++++++++++++++++--- src/arm/codegen-arm.h | 19 +++ src/arm/macro-assembler-arm.cc | 67 ++++++++- src/arm/macro-assembler-arm.h | 21 +++ src/ia32/codegen-ia32.h | 14 +- 5 files changed, 342 insertions(+), 24 deletions(-) diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index c33c1dacc5..9afefac0b3 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -3551,7 +3551,8 @@ void CodeGenerator::GenerateStringAdd(ZoneList* 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 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 diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h index 6eb7c964d3..2578a398c7 100644 --- a/src/arm/codegen-arm.h +++ b/src/arm/codegen-arm.h @@ -537,6 +537,7 @@ class StringStubBase: public CodeStub { // be used in places where the number of characters is small and the // additional setup and checking in GenerateCopyCharactersLong adds too much // overhead. Copying of overlapping regions is not supported. + // Dest register ends at the position after the last character written. void GenerateCopyCharacters(MacroAssembler* masm, Register dest, Register src, @@ -547,6 +548,7 @@ class StringStubBase: public CodeStub { // Generate code for copying a large number of characters. This function // is allowed to spend extra time setting up conditions to make copying // faster. Copying of overlapping regions is not supported. + // Dest register ends at the position after the last character written. void GenerateCopyCharactersLong(MacroAssembler* masm, Register dest, Register src, @@ -567,6 +569,23 @@ enum StringAddFlags { }; +class StringAddStub: public StringStubBase { + public: + explicit StringAddStub(StringAddFlags flags) { + string_check_ = ((flags & NO_STRING_CHECK_IN_STUB) == 0); + } + + private: + Major MajorKey() { return StringAdd; } + int MinorKey() { return string_check_ ? 0 : 1; } + + void Generate(MacroAssembler* masm); + + // Should the stub check whether arguments are strings? + bool string_check_; +}; + + class SubStringStub: public StringStubBase { public: SubStringStub() {} diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index a3273ed561..aadbb670d5 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -1009,6 +1009,44 @@ void MacroAssembler::AllocateAsciiString(Register result, } +void MacroAssembler::AllocateTwoByteConsString(Register result, + Register length, + Register scratch1, + Register scratch2, + Label* gc_required) { + AllocateInNewSpace(ConsString::kSize / kPointerSize, + result, + scratch1, + scratch2, + gc_required, + TAG_OBJECT); + LoadRoot(scratch1, Heap::kConsStringMapRootIndex); + mov(scratch2, Operand(String::kEmptyHashField)); + str(length, FieldMemOperand(result, String::kLengthOffset)); + str(scratch1, FieldMemOperand(result, HeapObject::kMapOffset)); + str(scratch2, FieldMemOperand(result, String::kHashFieldOffset)); +} + + +void MacroAssembler::AllocateAsciiConsString(Register result, + Register length, + Register scratch1, + Register scratch2, + Label* gc_required) { + AllocateInNewSpace(ConsString::kSize / kPointerSize, + result, + scratch1, + scratch2, + gc_required, + TAG_OBJECT); + LoadRoot(scratch1, Heap::kConsAsciiStringMapRootIndex); + mov(scratch2, Operand(String::kEmptyHashField)); + str(length, FieldMemOperand(result, String::kLengthOffset)); + str(scratch1, FieldMemOperand(result, HeapObject::kMapOffset)); + str(scratch2, FieldMemOperand(result, String::kHashFieldOffset)); +} + + void MacroAssembler::CompareObjectType(Register function, Register map, Register type_reg, @@ -1079,10 +1117,17 @@ void MacroAssembler::CallStub(CodeStub* stub, Condition cond) { } +void MacroAssembler::TailCallStub(CodeStub* stub, Condition cond) { + ASSERT(allow_stub_calls()); // stub calls are not allowed in some stubs + Jump(stub->GetCode(), RelocInfo::CODE_TARGET, cond); +} + + void MacroAssembler::StubReturn(int argc) { ASSERT(argc >= 1 && generating_stub()); - if (argc > 1) + if (argc > 1) { add(sp, sp, Operand((argc - 1) * kPointerSize)); + } Ret(); } @@ -1319,6 +1364,26 @@ void MacroAssembler::LoadContext(Register dst, int context_chain_length) { } +void MacroAssembler::JumpIfNotBothSmi(Register reg1, + Register reg2, + Label* on_not_both_smi) { + ASSERT_EQ(0, kSmiTag); + tst(reg1, Operand(kSmiTagMask)); + tst(reg2, Operand(kSmiTagMask), eq); + b(ne, on_not_both_smi); +} + + +void MacroAssembler::JumpIfEitherSmi(Register reg1, + Register reg2, + Label* on_either_smi) { + ASSERT_EQ(0, kSmiTag); + tst(reg1, Operand(kSmiTagMask)); + tst(reg2, Operand(kSmiTagMask), ne); + b(eq, on_either_smi); +} + + void MacroAssembler::JumpIfNonSmisNotBothSequentialAsciiStrings( Register first, Register second, diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h index 6a13ad9a1a..efb782c2ca 100644 --- a/src/arm/macro-assembler-arm.h +++ b/src/arm/macro-assembler-arm.h @@ -223,6 +223,16 @@ class MacroAssembler: public Assembler { Register scratch2, Register scratch3, Label* gc_required); + void AllocateTwoByteConsString(Register result, + Register length, + Register scratch1, + Register scratch2, + Label* gc_required); + void AllocateAsciiConsString(Register result, + Register length, + Register scratch1, + Register scratch2, + Label* gc_required); // --------------------------------------------------------------------------- @@ -302,6 +312,9 @@ class MacroAssembler: public Assembler { // Call a code stub. void CallStub(CodeStub* stub, Condition cond = al); + // Call a code stub. + void TailCallStub(CodeStub* stub, Condition cond = al); + // Return from a code stub after popping its arguments. void StubReturn(int argc); @@ -370,6 +383,14 @@ class MacroAssembler: public Assembler { void set_allow_stub_calls(bool value) { allow_stub_calls_ = value; } bool allow_stub_calls() { return allow_stub_calls_; } + // --------------------------------------------------------------------------- + // Smi utilities + + // Jump if either of the registers contain a non-smi. + void JumpIfNotBothSmi(Register reg1, Register reg2, Label* on_not_both_smi); + // Jump if either of the registers contain a smi. + void JumpIfEitherSmi(Register reg1, Register reg2, Label* on_either_smi); + // --------------------------------------------------------------------------- // String utilities diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h index 296afb9bf1..843bbf76f7 100644 --- a/src/ia32/codegen-ia32.h +++ b/src/ia32/codegen-ia32.h @@ -745,13 +745,6 @@ class GenericBinaryOpStub: public CodeStub { }; -// Flag that indicates how to generate code for the stub StringAddStub. -enum StringAddFlags { - NO_STRING_ADD_FLAGS = 0, - NO_STRING_CHECK_IN_STUB = 1 << 0 // Omit string check in stub. -}; - - class StringStubBase: public CodeStub { public: // Generate code for copying characters using a simple loop. This should only @@ -777,6 +770,13 @@ class StringStubBase: public CodeStub { }; +// Flag that indicates how to generate code for the stub StringAddStub. +enum StringAddFlags { + NO_STRING_ADD_FLAGS = 0, + NO_STRING_CHECK_IN_STUB = 1 << 0 // Omit string check in stub. +}; + + class StringAddStub: public StringStubBase { public: explicit StringAddStub(StringAddFlags flags) {