Commit 4cd99d7c authored by yangguo@chromium.org's avatar yangguo@chromium.org

Handle external strings in generated code when concatenating short strings.

TEST=string-external-cached.js

Review URL: http://codereview.chromium.org/8931025

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10252 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 03696ca7
...@@ -5474,7 +5474,7 @@ void StringCharAtGenerator::GenerateSlow( ...@@ -5474,7 +5474,7 @@ void StringCharAtGenerator::GenerateSlow(
void StringAddStub::Generate(MacroAssembler* masm) { void StringAddStub::Generate(MacroAssembler* masm) {
Label string_add_runtime, call_builtin; Label call_runtime, call_builtin;
Builtins::JavaScript builtin_id = Builtins::ADD; Builtins::JavaScript builtin_id = Builtins::ADD;
// Load the two arguments. // Load the two arguments.
...@@ -5483,14 +5483,14 @@ void StringAddStub::Generate(MacroAssembler* masm) { ...@@ -5483,14 +5483,14 @@ void StringAddStub::Generate(MacroAssembler* masm) {
// Make sure that both arguments are strings if not known in advance. // Make sure that both arguments are strings if not known in advance.
if (flags_ == NO_STRING_ADD_FLAGS) { if (flags_ == NO_STRING_ADD_FLAGS) {
__ JumpIfSmi(eax, &string_add_runtime); __ JumpIfSmi(eax, &call_runtime);
__ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ebx); __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ebx);
__ j(above_equal, &string_add_runtime); __ j(above_equal, &call_runtime);
// First argument is a a string, test second. // First argument is a a string, test second.
__ JumpIfSmi(edx, &string_add_runtime); __ JumpIfSmi(edx, &call_runtime);
__ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ebx); __ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ebx);
__ j(above_equal, &string_add_runtime); __ j(above_equal, &call_runtime);
} else { } else {
// Here at least one of the arguments is definitely a string. // Here at least one of the arguments is definitely a string.
// We convert the one that is not known to be a string. // We convert the one that is not known to be a string.
...@@ -5541,15 +5541,14 @@ void StringAddStub::Generate(MacroAssembler* masm) { ...@@ -5541,15 +5541,14 @@ void StringAddStub::Generate(MacroAssembler* masm) {
__ add(ebx, ecx); __ add(ebx, ecx);
STATIC_ASSERT(Smi::kMaxValue == String::kMaxLength); STATIC_ASSERT(Smi::kMaxValue == String::kMaxLength);
// Handle exceptionally long strings in the runtime system. // Handle exceptionally long strings in the runtime system.
__ j(overflow, &string_add_runtime); __ j(overflow, &call_runtime);
// Use the symbol table when adding two one character strings, as it // Use the symbol table when adding two one character strings, as it
// helps later optimizations to return a symbol here. // helps later optimizations to return a symbol here.
__ cmp(ebx, Immediate(Smi::FromInt(2))); __ cmp(ebx, Immediate(Smi::FromInt(2)));
__ j(not_equal, &longer_than_two); __ j(not_equal, &longer_than_two);
// Check that both strings are non-external ascii strings. // Check that both strings are non-external ascii strings.
__ JumpIfNotBothSequentialAsciiStrings(eax, edx, ebx, ecx, __ JumpIfNotBothSequentialAsciiStrings(eax, edx, ebx, ecx, &call_runtime);
&string_add_runtime);
// Get the two characters forming the new string. // Get the two characters forming the new string.
__ movzx_b(ebx, FieldOperand(eax, SeqAsciiString::kHeaderSize)); __ movzx_b(ebx, FieldOperand(eax, SeqAsciiString::kHeaderSize));
...@@ -5574,11 +5573,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { ...@@ -5574,11 +5573,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
__ movzx_b(ecx, FieldOperand(edx, SeqAsciiString::kHeaderSize)); __ movzx_b(ecx, FieldOperand(edx, SeqAsciiString::kHeaderSize));
__ bind(&make_two_character_string_no_reload); __ bind(&make_two_character_string_no_reload);
__ IncrementCounter(counters->string_add_make_two_char(), 1); __ IncrementCounter(counters->string_add_make_two_char(), 1);
__ AllocateAsciiString(eax, // Result. __ AllocateAsciiString(eax, 2, edi, edx, &call_runtime);
2, // Length.
edi, // Scratch 1.
edx, // Scratch 2.
&string_add_runtime);
// Pack both characters in ebx. // Pack both characters in ebx.
__ shl(ecx, kBitsPerByte); __ shl(ecx, kBitsPerByte);
__ or_(ebx, ecx); __ or_(ebx, ecx);
...@@ -5603,10 +5598,10 @@ void StringAddStub::Generate(MacroAssembler* masm) { ...@@ -5603,10 +5598,10 @@ void StringAddStub::Generate(MacroAssembler* masm) {
STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
__ test(ecx, Immediate(kStringEncodingMask)); __ test(ecx, Immediate(kStringEncodingMask));
__ j(zero, &non_ascii); __ j(zero, &non_ascii, Label::kNear);
__ bind(&ascii_data); __ bind(&ascii_data);
// Allocate an acsii cons string. // Allocate an acsii cons string.
__ AllocateAsciiConsString(ecx, edi, no_reg, &string_add_runtime); __ AllocateAsciiConsString(ecx, edi, no_reg, &call_runtime);
__ bind(&allocated); __ bind(&allocated);
// Fill the fields of the cons string. // Fill the fields of the cons string.
if (FLAG_debug_code) __ AbortIfNotSmi(ebx); if (FLAG_debug_code) __ AbortIfNotSmi(ebx);
...@@ -5633,64 +5628,95 @@ void StringAddStub::Generate(MacroAssembler* masm) { ...@@ -5633,64 +5628,95 @@ void StringAddStub::Generate(MacroAssembler* masm) {
__ cmp(edi, kAsciiStringTag | kAsciiDataHintTag); __ cmp(edi, kAsciiStringTag | kAsciiDataHintTag);
__ j(equal, &ascii_data); __ j(equal, &ascii_data);
// Allocate a two byte cons string. // Allocate a two byte cons string.
__ AllocateTwoByteConsString(ecx, edi, no_reg, &string_add_runtime); __ AllocateTwoByteConsString(ecx, edi, no_reg, &call_runtime);
__ jmp(&allocated); __ jmp(&allocated);
// Handle creating a flat result. First check that both strings are not // We cannot encounter sliced strings or cons strings here since:
// external strings. STATIC_ASSERT(SlicedString::kMinLength >= String::kMinNonFlatLength);
// Handle creating a flat result from either external or sequential strings.
// Locate the first characters' locations.
// eax: first string // eax: first string
// ebx: length of resulting flat string as a smi // ebx: length of resulting flat string as a smi
// edx: second string // edx: second string
Label first_prepared, second_prepared;
Label first_is_sequential, second_is_sequential;
__ bind(&string_add_flat_result); __ bind(&string_add_flat_result);
__ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
__ and_(ecx, kStringRepresentationMask); // ecx: instance type of first string
__ cmp(ecx, kExternalStringTag); STATIC_ASSERT(kSeqStringTag == 0);
__ j(equal, &string_add_runtime); __ test_b(ecx, kStringRepresentationMask);
__ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); __ j(zero, &first_is_sequential, Label::kNear);
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); // Rule out short external string and prepare it so that offset-wise, it
__ and_(ecx, kStringRepresentationMask); // looks like a sequential string.
__ cmp(ecx, kExternalStringTag); STATIC_ASSERT(kShortExternalStringTag != 0);
__ j(equal, &string_add_runtime); __ test_b(ecx, kShortExternalStringMask);
// We cannot encounter sliced strings here since: __ j(not_zero, &call_runtime);
STATIC_ASSERT(SlicedString::kMinLength >= String::kMinNonFlatLength); __ mov(eax, FieldOperand(eax, ExternalString::kResourceDataOffset));
// Now check if both strings are ascii strings. STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize);
// eax: first string __ jmp(&first_prepared, Label::kNear);
// ebx: length of resulting flat string as a smi __ bind(&first_is_sequential);
// edx: second string __ add(eax, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
Label non_ascii_string_add_flat_result; __ bind(&first_prepared);
STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
__ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); __ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset));
__ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kStringEncodingMask); // Check whether both strings have same encoding.
// edi: instance type of second string
__ xor_(ecx, edi);
__ test_b(ecx, kStringEncodingMask);
__ j(not_zero, &call_runtime);
STATIC_ASSERT(kSeqStringTag == 0);
__ test_b(edi, kStringRepresentationMask);
__ j(zero, &second_is_sequential, Label::kNear);
// Rule out short external string and prepare it so that offset-wise, it
// looks like a sequential string.
STATIC_ASSERT(kShortExternalStringTag != 0);
__ test_b(edi, kShortExternalStringMask);
__ j(not_zero, &call_runtime);
__ mov(edx, FieldOperand(edx, ExternalString::kResourceDataOffset));
STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize);
__ jmp(&second_prepared, Label::kNear);
__ bind(&second_is_sequential);
__ add(edx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
__ bind(&second_prepared);
// Push the addresses of both strings' first characters onto the stack.
__ push(edx);
__ push(eax);
Label non_ascii_string_add_flat_result, call_runtime_drop_two;
// edi: instance type of second string
// First string and second string have the same encoding.
STATIC_ASSERT(kTwoByteStringTag == 0);
__ test_b(edi, kStringEncodingMask);
__ j(zero, &non_ascii_string_add_flat_result); __ j(zero, &non_ascii_string_add_flat_result);
__ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
__ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kStringEncodingMask);
__ j(zero, &string_add_runtime);
// Both strings are ascii strings. As they are short they are both flat. // Both strings are ascii strings.
// ebx: length of resulting flat string as a smi // ebx: length of resulting flat string as a smi
__ SmiUntag(ebx); __ SmiUntag(ebx);
__ AllocateAsciiString(eax, ebx, ecx, edx, edi, &string_add_runtime); __ AllocateAsciiString(eax, ebx, ecx, edx, edi, &call_runtime_drop_two);
// eax: result string // eax: result string
__ mov(ecx, eax); __ mov(ecx, eax);
// Locate first character of result. // Locate first character of result.
__ add(ecx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); __ add(ecx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
// Load first argument and locate first character. // Load first argument's length and first character location. Account for
__ mov(edx, Operand(esp, 2 * kPointerSize)); // values currently on the stack when fetching arguments from it.
__ mov(edx, Operand(esp, 4 * kPointerSize));
__ mov(edi, FieldOperand(edx, String::kLengthOffset)); __ mov(edi, FieldOperand(edx, String::kLengthOffset));
__ SmiUntag(edi); __ SmiUntag(edi);
__ add(edx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); __ pop(edx);
// eax: result string // eax: result string
// ecx: first character of result // ecx: first character of result
// edx: first char of first argument // edx: first char of first argument
// edi: length of first argument // edi: length of first argument
StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true); StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
// Load second argument and locate first character. // Load second argument's length and first character location. Account for
__ mov(edx, Operand(esp, 1 * kPointerSize)); // values currently on the stack when fetching arguments from it.
__ mov(edx, Operand(esp, 2 * kPointerSize));
__ mov(edi, FieldOperand(edx, String::kLengthOffset)); __ mov(edi, FieldOperand(edx, String::kLengthOffset));
__ SmiUntag(edi); __ SmiUntag(edi);
__ add(edx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); __ pop(edx);
// eax: result string // eax: result string
// ecx: next character of result // ecx: next character of result
// edx: first char of second argument // edx: first char of second argument
...@@ -5704,34 +5730,31 @@ void StringAddStub::Generate(MacroAssembler* masm) { ...@@ -5704,34 +5730,31 @@ void StringAddStub::Generate(MacroAssembler* masm) {
// ebx: length of resulting flat string as a smi // ebx: length of resulting flat string as a smi
// edx: second string // edx: second string
__ bind(&non_ascii_string_add_flat_result); __ bind(&non_ascii_string_add_flat_result);
__ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); // Both strings are two byte strings.
__ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kStringEncodingMask);
__ j(not_zero, &string_add_runtime);
// Both strings are two byte strings. As they are short they are both
// flat.
__ SmiUntag(ebx); __ SmiUntag(ebx);
__ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &string_add_runtime); __ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &call_runtime_drop_two);
// eax: result string // eax: result string
__ mov(ecx, eax); __ mov(ecx, eax);
// Locate first character of result. // Locate first character of result.
__ add(ecx, __ add(ecx,
Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
// Load first argument and locate first character. // Load second argument's length and first character location. Account for
__ mov(edx, Operand(esp, 2 * kPointerSize)); // values currently on the stack when fetching arguments from it.
__ mov(edx, Operand(esp, 4 * kPointerSize));
__ mov(edi, FieldOperand(edx, String::kLengthOffset)); __ mov(edi, FieldOperand(edx, String::kLengthOffset));
__ SmiUntag(edi); __ SmiUntag(edi);
__ add(edx, __ pop(edx);
Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
// eax: result string // eax: result string
// ecx: first character of result // ecx: first character of result
// edx: first char of first argument // edx: first char of first argument
// edi: length of first argument // edi: length of first argument
StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false); StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
// Load second argument and locate first character. // Load second argument's length and first character location. Account for
__ mov(edx, Operand(esp, 1 * kPointerSize)); // values currently on the stack when fetching arguments from it.
__ mov(edx, Operand(esp, 2 * kPointerSize));
__ mov(edi, FieldOperand(edx, String::kLengthOffset)); __ mov(edi, FieldOperand(edx, String::kLengthOffset));
__ SmiUntag(edi); __ SmiUntag(edi);
__ add(edx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag)); __ pop(edx);
// eax: result string // eax: result string
// ecx: next character of result // ecx: next character of result
// edx: first char of second argument // edx: first char of second argument
...@@ -5740,8 +5763,11 @@ void StringAddStub::Generate(MacroAssembler* masm) { ...@@ -5740,8 +5763,11 @@ void StringAddStub::Generate(MacroAssembler* masm) {
__ IncrementCounter(counters->string_add_native(), 1); __ IncrementCounter(counters->string_add_native(), 1);
__ ret(2 * kPointerSize); __ ret(2 * kPointerSize);
// Recover stack pointer before jumping to runtime.
__ bind(&call_runtime_drop_two);
__ Drop(2);
// Just jump to runtime to add the two strings. // Just jump to runtime to add the two strings.
__ bind(&string_add_runtime); __ bind(&call_runtime);
__ TailCallRuntime(Runtime::kStringAdd, 2, 1); __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
if (call_builtin.is_linked()) { if (call_builtin.is_linked()) {
......
...@@ -86,6 +86,29 @@ function test() { ...@@ -86,6 +86,29 @@ function test() {
assertEquals("DEFG", ascii_cons.substr(3, 4)); assertEquals("DEFG", ascii_cons.substr(3, 4));
assertEquals("DEFG", twobyte_cons.substr(4, 4)); assertEquals("DEFG", twobyte_cons.substr(4, 4));
} }
// Test adding external strings
var short_ascii = "E=";
var long_ascii = "MCsquared";
var short_twobyte = "E\u1234";
var long_twobyte = "MCsquare\u1234";
try { // String can only be externalized once
externalizeString(short_ascii, false);
externalizeString(long_ascii, false);
externalizeString(short_twobyte, true);
externalizeString(long_twobyte, true);
assertTrue(isAsciiString(short_asii) && isAsciiString(long_ascii));
assertFalse(isAsciiString(short_twobyte) || isAsciiString(long_twobyte));
} catch (ex) { }
assertEquals("E=MCsquared", short_ascii + long_ascii);
assertTrue(isAsciiString(short_ascii + long_ascii));
assertEquals("MCsquaredE=", long_ascii + short_ascii);
assertEquals("E\u1234MCsquare\u1234", short_twobyte + long_twobyte);
assertFalse(isAsciiString(short_twobyte + long_twobyte));
assertEquals("E=MCsquared", "E=" + long_ascii);
assertEquals("E\u1234MCsquared", short_twobyte + "MCsquared");
assertEquals("E\u1234MCsquared", short_twobyte + long_ascii);
assertFalse(isAsciiString(short_twobyte + long_ascii));
} }
// Run the test many times to ensure IC-s don't break things. // Run the test many times to ensure IC-s don't break things.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment