Revision: 3400 Author: [email protected] Date: Wed Dec 2 23:56:21 2009 Log: Perform string add in generated code on IA-32 platforms
This adds a code stub which can do most of what Heap::AllocateConsString can do. It bails out if the result cannot fit in new space or if the result is a short (flat) string and one argument is an ascii string and the other a two byte string. It also bails out if adding two one character strings as Heap::AllocateConsString has special handling of this utilizing the symbol table. The stub is used both for the binary add operation and for StringAdd calls from runtime JavaScript files. Extended the string add test to cover all sizes of flat result stings. Review URL: http://codereview.chromium.org/442024 http://code.google.com/p/v8/source/detail?r=3400 Modified: /branches/bleeding_edge/src/arm/codegen-arm.cc /branches/bleeding_edge/src/arm/codegen-arm.h /branches/bleeding_edge/src/code-stubs.h /branches/bleeding_edge/src/codegen.cc /branches/bleeding_edge/src/ia32/codegen-ia32.cc /branches/bleeding_edge/src/ia32/codegen-ia32.h /branches/bleeding_edge/src/ia32/macro-assembler-ia32.cc /branches/bleeding_edge/src/ia32/macro-assembler-ia32.h /branches/bleeding_edge/src/runtime.cc /branches/bleeding_edge/src/runtime.js /branches/bleeding_edge/src/v8-counters.h /branches/bleeding_edge/src/x64/codegen-x64.cc /branches/bleeding_edge/src/x64/codegen-x64.h /branches/bleeding_edge/test/cctest/test-strings.cc /branches/bleeding_edge/test/mjsunit/string-add.js ======================================= --- /branches/bleeding_edge/src/arm/codegen-arm.cc Tue Dec 1 02:25:29 2009 +++ /branches/bleeding_edge/src/arm/codegen-arm.cc Wed Dec 2 23:56:21 2009 @@ -3503,6 +3503,17 @@ } frame_->EmitPush(r0); } + + +void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) { + ASSERT_EQ(2, args->length()); + + Load(args->at(0)); + Load(args->at(1)); + + frame_->CallRuntime(Runtime::kStringAdd, 2); + frame_->EmitPush(r0); +} void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) { ======================================= --- /branches/bleeding_edge/src/arm/codegen-arm.h Wed Nov 18 23:41:32 2009 +++ /branches/bleeding_edge/src/arm/codegen-arm.h Wed Dec 2 23:56:21 2009 @@ -366,6 +366,9 @@ inline void GenerateMathSin(ZoneList<Expression*>* args); inline void GenerateMathCos(ZoneList<Expression*>* args); + // Fast support for StringAdd. + void GenerateStringAdd(ZoneList<Expression*>* args); + // Simple condition analysis. enum ConditionAnalysis { ALWAYS_TRUE, ======================================= --- /branches/bleeding_edge/src/code-stubs.h Wed Nov 11 06:32:14 2009 +++ /branches/bleeding_edge/src/code-stubs.h Wed Dec 2 23:56:21 2009 @@ -36,6 +36,7 @@ #define CODE_STUB_LIST_ALL_PLATFORMS(V) \ V(CallFunction) \ V(GenericBinaryOp) \ + V(StringAdd) \ V(SmiOp) \ V(Compare) \ V(RecordWrite) \ ======================================= --- /branches/bleeding_edge/src/codegen.cc Wed Nov 18 23:41:32 2009 +++ /branches/bleeding_edge/src/codegen.cc Wed Dec 2 23:56:21 2009 @@ -346,6 +346,7 @@ {&CodeGenerator::GenerateMathCos, "_Math_cos"}, {&CodeGenerator::GenerateIsObject, "_IsObject"}, {&CodeGenerator::GenerateIsFunction, "_IsFunction"}, + {&CodeGenerator::GenerateStringAdd, "_StringAdd"}, }; ======================================= --- /branches/bleeding_edge/src/ia32/codegen-ia32.cc Mon Nov 30 07:09:49 2009 +++ /branches/bleeding_edge/src/ia32/codegen-ia32.cc Wed Dec 2 23:56:21 2009 @@ -5210,6 +5210,18 @@ frame_->Push(&answer); done.Bind(); } + + +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) { @@ -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); @@ -8171,6 +8184,224 @@ ASSERT(static_cast<unsigned>(cc_) < (1 << 15)); 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)); + __ 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)); + __ 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 __ ======================================= --- /branches/bleeding_edge/src/ia32/codegen-ia32.h Wed Nov 18 23:41:32 2009 +++ /branches/bleeding_edge/src/ia32/codegen-ia32.h Wed Dec 2 23:56:21 2009 @@ -546,6 +546,9 @@ inline void GenerateMathSin(ZoneList<Expression*>* args); inline void GenerateMathCos(ZoneList<Expression*>* args); + // Fast support for StringAdd. + void GenerateStringAdd(ZoneList<Expression*>* args); + // Simple condition analysis. enum ConditionAnalysis { ALWAYS_TRUE, @@ -737,6 +740,37 @@ }; +// 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 CodeStub { + 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); + + void GenerateCopyCharacters(MacroAssembler* masm, + Register desc, + Register src, + Register count, + Register scratch, + bool ascii); + + // Should the stub check whether arguments are strings? + bool string_check_; +}; + + } } // namespace v8::internal #endif // V8_IA32_CODEGEN_IA32_H_ ======================================= --- /branches/bleeding_edge/src/ia32/macro-assembler-ia32.cc Thu Nov 26 02:28:32 2009 +++ /branches/bleeding_edge/src/ia32/macro-assembler-ia32.cc Wed Dec 2 23:56:21 2009 @@ -818,6 +818,104 @@ mov(FieldOperand(result, HeapObject::kMapOffset), Immediate(Factory::heap_number_map())); } + + +void MacroAssembler::AllocateTwoByteString(Register result, + Register length, + Register scratch1, + Register scratch2, + Register scratch3, + Label* gc_required) { + // Calculate the number of words needed for the number of characters in the + // string + mov(scratch1, length); + add(Operand(scratch1), Immediate(1)); + shr(scratch1, 1); + + // Allocate two byte string in new space. + AllocateInNewSpace(SeqTwoByteString::kHeaderSize, + times_4, + scratch1, + result, + scratch2, + scratch3, + gc_required, + TAG_OBJECT); + + // Set the map, length and hash field. + mov(FieldOperand(result, HeapObject::kMapOffset), + Immediate(Factory::string_map())); + mov(FieldOperand(result, String::kLengthOffset), length); + mov(FieldOperand(result, String::kHashFieldOffset), + Immediate(String::kEmptyHashField)); +} + + +void MacroAssembler::AllocateAsciiString(Register result, + Register length, + Register scratch1, + Register scratch2, + Register scratch3, + Label* gc_required) { + // Calculate the number of words needed for the number of characters in the + // string + mov(scratch1, length); + add(Operand(scratch1), Immediate(3)); + shr(scratch1, 2); + + // Allocate ascii string in new space. + AllocateInNewSpace(SeqAsciiString::kHeaderSize, + times_4, + scratch1, + result, + scratch2, + scratch3, + gc_required, + TAG_OBJECT); + + // Set the map, length and hash field. + mov(FieldOperand(result, HeapObject::kMapOffset), + Immediate(Factory::ascii_string_map())); + mov(FieldOperand(result, String::kLengthOffset), length); + mov(FieldOperand(result, String::kHashFieldOffset), + Immediate(String::kEmptyHashField)); +} + + +void MacroAssembler::AllocateConsString(Register result, + Register scratch1, + Register scratch2, + Label* gc_required) { + // Allocate heap number in new space. + AllocateInNewSpace(ConsString::kSize, + result, + scratch1, + scratch2, + gc_required, + TAG_OBJECT); + + // Set the map. The other fields are left uninitialized. + mov(FieldOperand(result, HeapObject::kMapOffset), + Immediate(Factory::cons_string_map())); +} + + +void MacroAssembler::AllocateAsciiConsString(Register result, + Register scratch1, + Register scratch2, + Label* gc_required) { + // Allocate heap number in new space. + AllocateInNewSpace(ConsString::kSize, + result, + scratch1, + scratch2, + gc_required, + TAG_OBJECT); + + // Set the map. The other fields are left uninitialized. + mov(FieldOperand(result, HeapObject::kMapOffset), + Immediate(Factory::cons_ascii_string_map())); +} void MacroAssembler::NegativeZeroTest(CodeGenerator* cgen, @@ -911,6 +1009,12 @@ ASSERT(allow_stub_calls()); // calls are not allowed in some stubs call(stub->GetCode(), RelocInfo::CODE_TARGET); } + + +void MacroAssembler::TailCallStub(CodeStub* stub) { + ASSERT(allow_stub_calls()); // calls are not allowed in some stubs + jmp(stub->GetCode(), RelocInfo::CODE_TARGET); +} void MacroAssembler::StubReturn(int argc) { ======================================= --- /branches/bleeding_edge/src/ia32/macro-assembler-ia32.h Thu Nov 26 02:28:32 2009 +++ /branches/bleeding_edge/src/ia32/macro-assembler-ia32.h Wed Dec 2 23:56:21 2009 @@ -183,7 +183,7 @@ // scratch can be passed as no_reg in which case an additional object // reference will be added to the reloc info. The returned pointers in result // and result_end have not yet been tagged as heap objects. If - // result_contains_top_on_entry is true the contnt of result is known to be + // result_contains_top_on_entry is true the content of result is known to be // the allocation top on entry (could be result_end from a previous call to // AllocateInNewSpace). If result_contains_top_on_entry is true scratch // should be no_reg as it is never used. @@ -225,6 +225,32 @@ Register scratch2, Label* gc_required); + // Allocate a sequential string. All the header fields of the string object + // are initialized. + void AllocateTwoByteString(Register result, + Register length, + Register scratch1, + Register scratch2, + Register scratch3, + Label* gc_required); + void AllocateAsciiString(Register result, + Register length, + Register scratch1, + Register scratch2, + Register scratch3, + Label* gc_required); + + // Allocate a raw cons string object. Only the map field of the result is + // initialized. + void AllocateConsString(Register result, + Register scratch1, + Register scratch2, + Label* gc_required); + void AllocateAsciiConsString(Register result, + Register scratch1, + Register scratch2, + Label* gc_required); + // --------------------------------------------------------------------------- // Support functions. @@ -262,6 +288,9 @@ // Call a code stub. void CallStub(CodeStub* stub); + // Tail call a code stub (jump). + void TailCallStub(CodeStub* stub); + // Return from a code stub after popping its arguments. void StubReturn(int argc); ======================================= --- /branches/bleeding_edge/src/runtime.cc Mon Nov 30 06:56:20 2009 +++ /branches/bleeding_edge/src/runtime.cc Wed Dec 2 23:56:21 2009 @@ -3783,6 +3783,7 @@ ASSERT(args.length() == 2); CONVERT_CHECKED(String, str1, args[0]); CONVERT_CHECKED(String, str2, args[1]); + Counters::string_add_runtime.Increment(); return Heap::AllocateConsString(str1, str2); } ======================================= --- /branches/bleeding_edge/src/runtime.js Wed Oct 28 03:19:56 2009 +++ /branches/bleeding_edge/src/runtime.js Wed Dec 2 23:56:21 2009 @@ -146,16 +146,16 @@ function ADD(x) { // Fast case: Check for number operands and do the addition. if (IS_NUMBER(this) && IS_NUMBER(x)) return %NumberAdd(this, x); - if (IS_STRING(this) && IS_STRING(x)) return %StringAdd(this, x); + if (IS_STRING(this) && IS_STRING(x)) return %_StringAdd(this, x); // Default implementation. var a = %ToPrimitive(this, NO_HINT); var b = %ToPrimitive(x, NO_HINT); if (IS_STRING(a)) { - return %StringAdd(a, %ToString(b)); + return %_StringAdd(a, %ToString(b)); } else if (IS_STRING(b)) { - return %StringAdd(%ToString(a), b); + return %_StringAdd(%ToString(a), b); } else { return %NumberAdd(%ToNumber(a), %ToNumber(b)); } @@ -173,7 +173,7 @@ : %ToString(%ToPrimitive(y, NO_HINT)); } } - return %StringAdd(this, y); + return %_StringAdd(this, y); } @@ -189,7 +189,7 @@ : %ToString(%ToPrimitive(x, NO_HINT)); } } - return %StringAdd(x, y); + return %_StringAdd(x, y); } ======================================= --- /branches/bleeding_edge/src/v8-counters.h Tue Oct 20 08:26:17 2009 +++ /branches/bleeding_edge/src/v8-counters.h Wed Dec 2 23:56:21 2009 @@ -153,8 +153,9 @@ SC(zone_segment_bytes, V8.ZoneSegmentBytes) \ SC(compute_entry_frame, V8.ComputeEntryFrame) \ SC(generic_binary_stub_calls, V8.GenericBinaryStubCalls) \ - SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs) - + SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs) \ + SC(string_add_runtime, V8.StringAddRuntime) \ + SC(string_add_native, V8.StringAddNative) // This file contains all the v8 counters that are in use. class Counters : AllStatic { ======================================= --- /branches/bleeding_edge/src/x64/codegen-x64.cc Mon Nov 30 07:09:49 2009 +++ /branches/bleeding_edge/src/x64/codegen-x64.cc Wed Dec 2 23:56:21 2009 @@ -3994,6 +3994,17 @@ frame_->Push(&answer); done.Bind(); } + + +void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) { + ASSERT_EQ(2, args->length()); + + Load(args->at(0)); + Load(args->at(1)); + + Result answer = frame_->CallRuntime(Runtime::kStringAdd, 2); + frame_->Push(&answer); +} void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) { ======================================= --- /branches/bleeding_edge/src/x64/codegen-x64.h Wed Nov 18 23:41:32 2009 +++ /branches/bleeding_edge/src/x64/codegen-x64.h Wed Dec 2 23:56:21 2009 @@ -544,6 +544,9 @@ inline void GenerateMathSin(ZoneList<Expression*>* args); inline void GenerateMathCos(ZoneList<Expression*>* args); + // Fast support for StringAdd. + void GenerateStringAdd(ZoneList<Expression*>* args); + // Simple condition analysis. enum ConditionAnalysis { ALWAYS_TRUE, ======================================= --- /branches/bleeding_edge/test/cctest/test-strings.cc Wed Dec 2 04:58:10 2009 +++ /branches/bleeding_edge/test/cctest/test-strings.cc Wed Dec 2 23:56:21 2009 @@ -376,7 +376,7 @@ ascii_external_strings->Set(v8::Integer::New(i), ascii_external_string); uc16* non_ascii = Zone::NewArray<uc16>(i + 1); for (int j = 0; j < i; j++) { - non_ascii[j] = 1234; + non_ascii[j] = 0x1234; } // Terminating '\0' is left out on purpose. It is not required for external // string data. @@ -389,20 +389,44 @@ // Add the arrays with the short external strings in the global object. v8::Handle<v8::Object> global = env->Global(); - global->Set(v8_str("ascii"), ascii_external_strings); - global->Set(v8_str("non_ascii"), non_ascii_external_strings); + global->Set(v8_str("external_ascii"), ascii_external_strings); + global->Set(v8_str("external_non_ascii"), non_ascii_external_strings); + global->Set(v8_str("max_length"), v8::Integer::New(kMaxLength)); // Add short external ascii and non-ascii strings checking the result. static const char* source = "function test() {" - " for (var i = 0; i <= 20; i++) {" + " var ascii_chars = 'aaaaaaaaaaaaaaaaaaaa';" + " var non_ascii_chars = '\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234';" //NOLINT + " if (ascii_chars.length != max_length) return 1;" + " if (non_ascii_chars.length != max_length) return 2;" + " var ascii = Array(max_length + 1);" + " var non_ascii = Array(max_length + 1);" + " for (var i = 0; i <= max_length; i++) {" + " ascii[i] = ascii_chars.substring(0, i);" + " non_ascii[i] = non_ascii_chars.substring(0, i);" + " };" + " for (var i = 0; i <= max_length; i++) {" + " if (ascii[i] != external_ascii[i]) return 3;" + " if (non_ascii[i] != external_non_ascii[i]) return 4;" " for (var j = 0; j < i; j++) {" - " if (non_ascii[i] != (non_ascii[j] + non_ascii[i - j])) return false;" - " if (ascii[i] != (ascii[j] + ascii[i - j])) return false;" + " if (external_ascii[i] !=" + " (external_ascii[j] + external_ascii[i - j])) return 5;" + " if (external_non_ascii[i] !=" + " (external_non_ascii[j] + external_non_ascii[i - j])) return 6;" + " if (non_ascii[i] != (non_ascii[j] + non_ascii[i - j])) return 7;" + " if (ascii[i] != (ascii[j] + ascii[i - j])) return 8;" + " if (ascii[i] != (external_ascii[j] + ascii[i - j])) return 9;" + " if (ascii[i] != (ascii[j] + external_ascii[i - j])) return 10;" + " if (non_ascii[i] !=" + " (external_non_ascii[j] + non_ascii[i - j])) return 11;" + " if (non_ascii[i] !=" + " (non_ascii[j] + external_non_ascii[i - j])) return 12;" " }" " }" - " return true;" + " return 0;" "};" "test()"; - CHECK(v8::Script::Compile(v8::String::New(source))->Run()->BooleanValue()); -} + CHECK_EQ(0, + v8::Script::Compile(v8::String::New(source))->Run()->Int32Value()); +} ======================================= --- /branches/bleeding_edge/test/mjsunit/string-add.js Fri Apr 3 05:01:56 2009 +++ /branches/bleeding_edge/test/mjsunit/string-add.js Wed Dec 2 23:56:21 2009 @@ -173,3 +173,23 @@ assertEquals("42strz", reswz, "swwz"); assertEquals(84, resww, "swww"); })(1); + +// Generate ascii and non ascii strings from length 0 to 20. +var ascii = 'aaaaaaaaaaaaaaaaaaaa'; +var non_ascii = '\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234'; +assertEquals(20, ascii.length); +assertEquals(20, non_ascii.length); +var a = Array(21); +var b = Array(21); +for (var i = 0; i <= 20; i++) { + a[i] = ascii.substring(0, i); + b[i] = non_ascii.substring(0, i); +} + +// Add ascii and non-ascii strings generating strings with length from 0 to 20. +for (var i = 0; i <= 20; i++) { + for (var j = 0; j < i; j++) { + assertEquals(a[i], a[j] + a[i - j]) + assertEquals(b[i], b[j] + b[i - j]) + } +} -- v8-dev mailing list [email protected] http://groups.google.com/group/v8-dev
