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(
void StringAddStub::Generate(MacroAssembler* masm) {
Label string_add_runtime, call_builtin;
Label call_runtime, call_builtin;
Builtins::JavaScript builtin_id = Builtins::ADD;
// Load the two arguments.
......@@ -5483,14 +5483,14 @@ void StringAddStub::Generate(MacroAssembler* masm) {
// Make sure that both arguments are strings if not known in advance.
if (flags_ == NO_STRING_ADD_FLAGS) {
__ JumpIfSmi(eax, &string_add_runtime);
__ JumpIfSmi(eax, &call_runtime);
__ 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.
__ JumpIfSmi(edx, &string_add_runtime);
__ JumpIfSmi(edx, &call_runtime);
__ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ebx);
__ j(above_equal, &string_add_runtime);
__ j(above_equal, &call_runtime);
} else {
// Here at least one of the arguments is definitely a string.
// We convert the one that is not known to be a string.
......@@ -5541,15 +5541,14 @@ void StringAddStub::Generate(MacroAssembler* masm) {
__ add(ebx, ecx);
STATIC_ASSERT(Smi::kMaxValue == String::kMaxLength);
// 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
// helps later optimizations to return a symbol here.
__ cmp(ebx, Immediate(Smi::FromInt(2)));
__ j(not_equal, &longer_than_two);
// Check that both strings are non-external ascii strings.
__ JumpIfNotBothSequentialAsciiStrings(eax, edx, ebx, ecx,
&string_add_runtime);
__ JumpIfNotBothSequentialAsciiStrings(eax, edx, ebx, ecx, &call_runtime);
// Get the two characters forming the new string.
__ movzx_b(ebx, FieldOperand(eax, SeqAsciiString::kHeaderSize));
......@@ -5574,11 +5573,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
__ movzx_b(ecx, FieldOperand(edx, SeqAsciiString::kHeaderSize));
__ bind(&make_two_character_string_no_reload);
__ IncrementCounter(counters->string_add_make_two_char(), 1);
__ AllocateAsciiString(eax, // Result.
2, // Length.
edi, // Scratch 1.
edx, // Scratch 2.
&string_add_runtime);
__ AllocateAsciiString(eax, 2, edi, edx, &call_runtime);
// Pack both characters in ebx.
__ shl(ecx, kBitsPerByte);
__ or_(ebx, ecx);
......@@ -5603,10 +5598,10 @@ void StringAddStub::Generate(MacroAssembler* masm) {
STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
__ test(ecx, Immediate(kStringEncodingMask));
__ j(zero, &non_ascii);
__ j(zero, &non_ascii, Label::kNear);
__ bind(&ascii_data);
// Allocate an acsii cons string.
__ AllocateAsciiConsString(ecx, edi, no_reg, &string_add_runtime);
__ AllocateAsciiConsString(ecx, edi, no_reg, &call_runtime);
__ bind(&allocated);
// Fill the fields of the cons string.
if (FLAG_debug_code) __ AbortIfNotSmi(ebx);
......@@ -5633,64 +5628,95 @@ void StringAddStub::Generate(MacroAssembler* masm) {
__ cmp(edi, kAsciiStringTag | kAsciiDataHintTag);
__ j(equal, &ascii_data);
// Allocate a two byte cons string.
__ AllocateTwoByteConsString(ecx, edi, no_reg, &string_add_runtime);
__ AllocateTwoByteConsString(ecx, edi, no_reg, &call_runtime);
__ jmp(&allocated);
// Handle creating a flat result. First check that both strings are not
// external strings.
// We cannot encounter sliced strings or cons strings here since:
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
// ebx: length of resulting flat string as a smi
// edx: second string
Label first_prepared, second_prepared;
Label first_is_sequential, second_is_sequential;
__ 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);
// We cannot encounter sliced strings here since:
STATIC_ASSERT(SlicedString::kMinLength >= String::kMinNonFlatLength);
// Now check if both strings are ascii strings.
// eax: first string
// ebx: length of resulting flat string as a smi
// edx: second string
Label non_ascii_string_add_flat_result;
STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
__ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
__ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kStringEncodingMask);
// ecx: instance type of first string
STATIC_ASSERT(kSeqStringTag == 0);
__ test_b(ecx, kStringRepresentationMask);
__ j(zero, &first_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(ecx, kShortExternalStringMask);
__ j(not_zero, &call_runtime);
__ mov(eax, FieldOperand(eax, ExternalString::kResourceDataOffset));
STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize);
__ jmp(&first_prepared, Label::kNear);
__ bind(&first_is_sequential);
__ add(eax, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
__ bind(&first_prepared);
__ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
__ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset));
// 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);
__ 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
__ SmiUntag(ebx);
__ AllocateAsciiString(eax, ebx, ecx, edx, edi, &string_add_runtime);
__ AllocateAsciiString(eax, ebx, ecx, edx, edi, &call_runtime_drop_two);
// eax: result string
__ mov(ecx, eax);
// Locate first character of result.
__ add(ecx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
// Load first argument and locate first character.
__ mov(edx, Operand(esp, 2 * kPointerSize));
// Load first argument's length and first character location. Account for
// values currently on the stack when fetching arguments from it.
__ mov(edx, Operand(esp, 4 * kPointerSize));
__ mov(edi, FieldOperand(edx, String::kLengthOffset));
__ SmiUntag(edi);
__ add(edx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
__ pop(edx);
// eax: result string
// ecx: first character of result
// edx: first char of first argument
// edi: length of first argument
StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
// Load second argument and locate first character.
__ mov(edx, Operand(esp, 1 * kPointerSize));
// Load second argument's length and first character location. Account for
// values currently on the stack when fetching arguments from it.
__ mov(edx, Operand(esp, 2 * kPointerSize));
__ mov(edi, FieldOperand(edx, String::kLengthOffset));
__ SmiUntag(edi);
__ add(edx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
__ pop(edx);
// eax: result string
// ecx: next character of result
// edx: first char of second argument
......@@ -5704,34 +5730,31 @@ void StringAddStub::Generate(MacroAssembler* masm) {
// ebx: length of resulting flat string as a smi
// edx: second string
__ bind(&non_ascii_string_add_flat_result);
__ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
__ 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.
// Both strings are two byte strings.
__ SmiUntag(ebx);
__ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &string_add_runtime);
__ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &call_runtime_drop_two);
// eax: result string
__ mov(ecx, eax);
// Locate first character of result.
__ add(ecx,
Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
// Load first argument and locate first character.
__ mov(edx, Operand(esp, 2 * kPointerSize));
// Load second argument's length and first character location. Account for
// values currently on the stack when fetching arguments from it.
__ mov(edx, Operand(esp, 4 * kPointerSize));
__ mov(edi, FieldOperand(edx, String::kLengthOffset));
__ SmiUntag(edi);
__ add(edx,
Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
__ pop(edx);
// eax: result string
// ecx: first character of result
// edx: first char of first argument
// edi: length of first argument
StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
// Load second argument and locate first character.
__ mov(edx, Operand(esp, 1 * kPointerSize));
// Load second argument's length and first character location. Account for
// values currently on the stack when fetching arguments from it.
__ mov(edx, Operand(esp, 2 * kPointerSize));
__ mov(edi, FieldOperand(edx, String::kLengthOffset));
__ SmiUntag(edi);
__ add(edx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
__ pop(edx);
// eax: result string
// ecx: next character of result
// edx: first char of second argument
......@@ -5740,8 +5763,11 @@ void StringAddStub::Generate(MacroAssembler* masm) {
__ IncrementCounter(counters->string_add_native(), 1);
__ 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.
__ bind(&string_add_runtime);
__ bind(&call_runtime);
__ TailCallRuntime(Runtime::kStringAdd, 2, 1);
if (call_builtin.is_linked()) {
......
......@@ -86,6 +86,29 @@ function test() {
assertEquals("DEFG", ascii_cons.substr(3, 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.
......
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