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:
parent
2618422ed5
commit
dd85270967
@ -3551,7 +3551,8 @@ void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* 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
|
||||
|
@ -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() {}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user