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
This commit is contained in:
lrn@chromium.org 2010-02-05 12:00:42 +00:00
parent 2618422ed5
commit dd85270967
5 changed files with 342 additions and 24 deletions

View File

@ -3551,7 +3551,8 @@ void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
Load(args->at(0)); Load(args->at(0));
Load(args->at(1)); Load(args->at(1));
frame_->CallRuntime(Runtime::kStringAdd, 2); StringAddStub stub(NO_STRING_ADD_FLAGS);
frame_->CallStub(&stub, 2);
frame_->EmitPush(r0); frame_->EmitPush(r0);
} }
@ -5332,7 +5333,7 @@ static void HandleBinaryOpSlowCases(MacroAssembler* masm,
// r1 : first argument // r1 : first argument
// r0 : second argument // r0 : second argument
// sp[0] : second argument // sp[0] : second argument
// sp[1] : first argument // sp[4] : first argument
Label not_strings, not_string1, string1; Label not_strings, not_string1, string1;
__ tst(r1, Operand(kSmiTagMask)); __ tst(r1, Operand(kSmiTagMask));
@ -5347,7 +5348,8 @@ static void HandleBinaryOpSlowCases(MacroAssembler* masm,
__ b(ge, &string1); __ b(ge, &string1);
// First and second argument are strings. // 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. // Only first argument is a string.
__ bind(&string1); __ bind(&string1);
@ -5361,7 +5363,6 @@ static void HandleBinaryOpSlowCases(MacroAssembler* masm,
__ b(ge, &not_strings); __ b(ge, &not_strings);
// Only second argument is a string. // Only second argument is a string.
__ b(&not_strings);
__ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_JS); __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_JS);
__ bind(&not_strings); __ bind(&not_strings);
@ -6841,14 +6842,14 @@ void StringStubBase::GenerateCopyCharacters(MacroAssembler* masm,
__ b(eq, &done); __ b(eq, &done);
__ bind(&loop); __ 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); __ sub(count, count, Operand(1), SetCC);
__ ldrb(scratch, MemOperand(src, count), pl); __ strb(scratch, MemOperand(dest, 1, PostIndex));
// 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
// last iteration. // last iteration.
__ b(mi, &done); __ b(gt, &loop);
__ strb(scratch, MemOperand(dest, count));
__ b(&loop);
__ bind(&done); __ bind(&done);
} }
@ -7218,12 +7219,10 @@ void StringCompareStub::Generate(MacroAssembler* masm) {
Label runtime; Label runtime;
// Stack frame on entry. // Stack frame on entry.
// sp[0]: return address // sp[0]: right string
// sp[4]: right string // sp[4]: left string
// sp[8]: left string __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // left
__ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // right
__ ldr(r0, MemOperand(sp, 2 * kPointerSize)); // left
__ ldr(r1, MemOperand(sp, 1 * kPointerSize)); // right
Label not_same; Label not_same;
__ cmp(r0, r1); __ 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 __ #undef __
} } // namespace v8::internal } } // namespace v8::internal

View File

@ -537,6 +537,7 @@ class StringStubBase: public CodeStub {
// be used in places where the number of characters is small and the // be used in places where the number of characters is small and the
// additional setup and checking in GenerateCopyCharactersLong adds too much // additional setup and checking in GenerateCopyCharactersLong adds too much
// overhead. Copying of overlapping regions is not supported. // overhead. Copying of overlapping regions is not supported.
// Dest register ends at the position after the last character written.
void GenerateCopyCharacters(MacroAssembler* masm, void GenerateCopyCharacters(MacroAssembler* masm,
Register dest, Register dest,
Register src, Register src,
@ -547,6 +548,7 @@ class StringStubBase: public CodeStub {
// Generate code for copying a large number of characters. This function // Generate code for copying a large number of characters. This function
// is allowed to spend extra time setting up conditions to make copying // is allowed to spend extra time setting up conditions to make copying
// faster. Copying of overlapping regions is not supported. // faster. Copying of overlapping regions is not supported.
// Dest register ends at the position after the last character written.
void GenerateCopyCharactersLong(MacroAssembler* masm, void GenerateCopyCharactersLong(MacroAssembler* masm,
Register dest, Register dest,
Register src, 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 { class SubStringStub: public StringStubBase {
public: public:
SubStringStub() {} SubStringStub() {}

View File

@ -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, void MacroAssembler::CompareObjectType(Register function,
Register map, Register map,
Register type_reg, 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) { void MacroAssembler::StubReturn(int argc) {
ASSERT(argc >= 1 && generating_stub()); ASSERT(argc >= 1 && generating_stub());
if (argc > 1) if (argc > 1) {
add(sp, sp, Operand((argc - 1) * kPointerSize)); add(sp, sp, Operand((argc - 1) * kPointerSize));
}
Ret(); 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( void MacroAssembler::JumpIfNonSmisNotBothSequentialAsciiStrings(
Register first, Register first,
Register second, Register second,

View File

@ -223,6 +223,16 @@ class MacroAssembler: public Assembler {
Register scratch2, Register scratch2,
Register scratch3, Register scratch3,
Label* gc_required); 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. // Call a code stub.
void CallStub(CodeStub* stub, Condition cond = al); 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. // Return from a code stub after popping its arguments.
void StubReturn(int argc); void StubReturn(int argc);
@ -370,6 +383,14 @@ class MacroAssembler: public Assembler {
void set_allow_stub_calls(bool value) { allow_stub_calls_ = value; } void set_allow_stub_calls(bool value) { allow_stub_calls_ = value; }
bool allow_stub_calls() { return allow_stub_calls_; } 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 // String utilities

View File

@ -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 { class StringStubBase: public CodeStub {
public: public:
// Generate code for copying characters using a simple loop. This should only // 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 { class StringAddStub: public StringStubBase {
public: public:
explicit StringAddStub(StringAddFlags flags) { explicit StringAddStub(StringAddFlags flags) {