Use generated code to create sub strings.

Added a stub to allocate and fill a string object with a substring from another string.

Use the rep movs instruction to copy the string data as it turned out to be the fastest way.

While preparing this I experimented with some SSE2 instructions, so the instructions movdqa and movdqu are still in the IA-32 assembler even though they are not used.
Review URL: http://codereview.chromium.org/525085

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3554 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
sgjesse@chromium.org 2010-01-07 09:59:37 +00:00
parent 060711ab37
commit 3d36c712aa
13 changed files with 325 additions and 15 deletions

View File

@ -3451,6 +3451,18 @@ void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
}
void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) {
ASSERT_EQ(2, args->length());
Load(args->at(0));
Load(args->at(1));
Load(args->at(2));
frame_->CallRuntime(Runtime::kSubString, 3);
frame_->EmitPush(r0);
}
void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
ASSERT_EQ(4, args->length());

View File

@ -363,6 +363,9 @@ class CodeGenerator: public AstVisitor {
// Fast support for StringAdd.
void GenerateStringAdd(ZoneList<Expression*>* args);
// Fast support for SubString.
void GenerateSubString(ZoneList<Expression*>* args);
// Support for direct calls from JavaScript to native RegExp code.
void GenerateRegExpExec(ZoneList<Expression*>* args);

View File

@ -37,6 +37,7 @@ namespace internal {
V(CallFunction) \
V(GenericBinaryOp) \
V(StringAdd) \
V(SubString) \
V(SmiOp) \
V(Compare) \
V(RecordWrite) \

View File

@ -345,6 +345,7 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = {
{&CodeGenerator::GenerateIsObject, "_IsObject"},
{&CodeGenerator::GenerateIsFunction, "_IsFunction"},
{&CodeGenerator::GenerateStringAdd, "_StringAdd"},
{&CodeGenerator::GenerateSubString, "_SubString"},
{&CodeGenerator::GenerateRegExpExec, "_RegExpExec"},
};

View File

@ -752,6 +752,14 @@ void Assembler::cmov(Condition cc, Register dst, const Operand& src) {
}
void Assembler::rep_movs() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF3);
EMIT(0xA5);
}
void Assembler::xchg(Register dst, Register src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@ -2035,6 +2043,50 @@ void Assembler::comisd(XMMRegister dst, XMMRegister src) {
}
void Assembler::movdqa(const Operand& dst, XMMRegister src ) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0x7F);
emit_sse_operand(src, dst);
}
void Assembler::movdqa(XMMRegister dst, const Operand& src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0x66);
EMIT(0x0F);
EMIT(0x6F);
emit_sse_operand(dst, src);
}
void Assembler::movdqu(const Operand& dst, XMMRegister src ) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF3);
EMIT(0x0F);
EMIT(0x7F);
emit_sse_operand(src, dst);
}
void Assembler::movdqu(XMMRegister dst, const Operand& src) {
ASSERT(CpuFeatures::IsEnabled(SSE2));
EnsureSpace ensure_space(this);
last_pc_ = pc_;
EMIT(0xF3);
EMIT(0x0F);
EMIT(0x6F);
emit_sse_operand(dst, src);
}
void Assembler::movdbl(XMMRegister dst, const Operand& src) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;

View File

@ -540,6 +540,9 @@ class Assembler : public Malloced {
void cmov(Condition cc, Register dst, Handle<Object> handle);
void cmov(Condition cc, Register dst, const Operand& src);
// Repetitive moves.
void rep_movs();
// Exchange two registers
void xchg(Register dst, Register src);
@ -750,6 +753,11 @@ class Assembler : public Malloced {
void comisd(XMMRegister dst, XMMRegister src);
void movdqa(XMMRegister dst, const Operand& src);
void movdqa(const Operand& dst, XMMRegister src);
void movdqu(XMMRegister dst, const Operand& src);
void movdqu(const Operand& dst, XMMRegister src);
// Use either movsd or movlpd.
void movdbl(XMMRegister dst, const Operand& src);
void movdbl(const Operand& dst, XMMRegister src);

View File

@ -5282,6 +5282,19 @@ void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
}
void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) {
ASSERT_EQ(3, args->length());
Load(args->at(0));
Load(args->at(1));
Load(args->at(2));
SubStringStub stub;
Result answer = frame_->CallStub(&stub, 3);
frame_->Push(&answer);
}
void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
ASSERT_EQ(args->length(), 4);
@ -9168,12 +9181,12 @@ void StringAddStub::Generate(MacroAssembler* masm) {
}
void StringAddStub::GenerateCopyCharacters(MacroAssembler* masm,
Register dest,
Register src,
Register count,
Register scratch,
bool ascii) {
void StringStubBase::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
@ -9194,6 +9207,175 @@ void StringAddStub::GenerateCopyCharacters(MacroAssembler* masm,
}
void StringStubBase::GenerateCopyCharactersREP(MacroAssembler* masm,
Register dest,
Register src,
Register count,
Register scratch,
bool ascii) {
// Copy characters using rep movs of doublewords. Align destination on 4 byte
// boundary before starting rep movs. Copy remaining characters after running
// rep movs.
ASSERT(dest.is(edi)); // rep movs destination
ASSERT(src.is(esi)); // rep movs source
ASSERT(count.is(ecx)); // rep movs count
ASSERT(!scratch.is(dest));
ASSERT(!scratch.is(src));
ASSERT(!scratch.is(count));
// Nothing to do for zero characters.
Label done;
__ test(count, Operand(count));
__ j(zero, &done);
// Make count the number of bytes to copy.
if (!ascii) {
__ shl(count, 1);
}
// Don't enter the rep movs if there are less than 4 bytes to copy.
Label last_bytes;
__ test(count, Immediate(~3));
__ j(zero, &last_bytes);
// Copy from edi to esi using rep movs instruction.
__ mov(scratch, count);
__ sar(count, 2); // Number of doublewords to copy.
__ rep_movs();
// Find number of bytes left.
__ mov(count, scratch);
__ and_(count, 3);
// Check if there are more bytes to copy.
__ bind(&last_bytes);
__ test(count, Operand(count));
__ j(zero, &done);
// Copy remaining characters.
Label loop;
__ bind(&loop);
__ mov_b(scratch, Operand(src, 0));
__ mov_b(Operand(dest, 0), scratch);
__ add(Operand(src), Immediate(1));
__ add(Operand(dest), Immediate(1));
__ sub(Operand(count), Immediate(1));
__ j(not_zero, &loop);
__ bind(&done);
}
void SubStringStub::Generate(MacroAssembler* masm) {
Label runtime;
// Stack frame on entry.
// esp[0]: return address
// esp[4]: to
// esp[8]: from
// esp[12]: string
// Make sure first argument is a string.
__ mov(eax, Operand(esp, 3 * kPointerSize));
ASSERT_EQ(0, kSmiTag);
__ test(eax, Immediate(kSmiTagMask));
__ j(zero, &runtime);
Condition is_string = masm->IsObjectStringType(eax, ebx, ebx);
__ j(NegateCondition(is_string), &runtime);
// eax: string
// ebx: instance type
// Calculate length of sub string using the smi values.
__ mov(ecx, Operand(esp, 1 * kPointerSize)); // to
__ test(ecx, Immediate(kSmiTagMask));
__ j(not_zero, &runtime);
__ mov(edx, Operand(esp, 2 * kPointerSize)); // from
__ test(edx, Immediate(kSmiTagMask));
__ j(not_zero, &runtime);
__ sub(ecx, Operand(edx));
// Handle sub-strings of length 2 and less in the runtime system.
__ SmiUntag(ecx); // Result length is no longer smi.
__ cmp(ecx, 2);
__ j(below_equal, &runtime);
// eax: string
// ebx: instance type
// ecx: result string length
// Check for flat ascii string
Label non_ascii_flat;
__ and_(ebx, kStringRepresentationMask | kStringEncodingMask);
__ cmp(ebx, kSeqStringTag | kAsciiStringTag);
__ j(not_equal, &non_ascii_flat);
// Allocate the result.
__ AllocateAsciiString(eax, ecx, ebx, edx, edi, &runtime);
// eax: result string
// ecx: result string length
__ mov(edx, esi); // esi used by following code.
// Locate first character of result.
__ mov(edi, eax);
__ add(Operand(edi), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
// Load string argument and locate character of sub string start.
__ mov(esi, Operand(esp, 3 * kPointerSize));
__ add(Operand(esi), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
__ mov(ebx, Operand(esp, 2 * kPointerSize)); // from
__ SmiUntag(ebx);
__ add(esi, Operand(ebx));
// eax: result string
// ecx: result length
// edx: original value of esi
// edi: first character of result
// esi: character of sub string start
GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, true);
__ mov(esi, edx); // Restore esi.
__ IncrementCounter(&Counters::sub_string_native, 1);
__ ret(3 * kPointerSize);
__ bind(&non_ascii_flat);
// eax: string
// ebx: instance type & kStringRepresentationMask | kStringEncodingMask
// ecx: result string length
// Check for flat two byte string
__ cmp(ebx, kSeqStringTag | kTwoByteStringTag);
__ j(not_equal, &runtime);
// Allocate the result.
__ AllocateTwoByteString(eax, ecx, ebx, edx, edi, &runtime);
// eax: result string
// ecx: result string length
__ mov(edx, esi); // esi used by following code.
// Locate first character of result.
__ mov(edi, eax);
__ add(Operand(edi),
Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
// Load string argument and locate character of sub string start.
__ mov(esi, Operand(esp, 3 * kPointerSize));
__ add(Operand(esi), Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
__ mov(ebx, Operand(esp, 2 * kPointerSize)); // from
// As from is a smi it is 2 times the value which matches the size of a two
// byte character.
ASSERT_EQ(0, kSmiTag);
ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
__ add(esi, Operand(ebx));
// eax: result string
// ecx: result length
// edx: original value of esi
// edi: first character of result
// esi: character of sub string start
GenerateCopyCharactersREP(masm, edi, esi, ecx, ebx, false);
__ mov(esi, edx); // Restore esi.
__ IncrementCounter(&Counters::sub_string_native, 1);
__ ret(3 * kPointerSize);
// Just jump to runtime to create the sub string.
__ bind(&runtime);
__ TailCallRuntime(ExternalReference(Runtime::kSubString), 3, 1);
}
#undef __
} } // namespace v8::internal

View File

@ -544,6 +544,9 @@ class CodeGenerator: public AstVisitor {
// Fast support for StringAdd.
void GenerateStringAdd(ZoneList<Expression*>* args);
// Fast support for SubString.
void GenerateSubString(ZoneList<Expression*>* args);
// Support for direct calls from JavaScript to native RegExp code.
void GenerateRegExpExec(ZoneList<Expression*>* args);
@ -747,7 +750,32 @@ enum StringAddFlags {
};
class StringAddStub: public CodeStub {
class StringStubBase: public CodeStub {
public:
// Generate code for copying characters using a simple loop. This should only
// be used in places where the number of characters is small and the
// additional setup and checking in GenerateCopyCharactersREP adds too much
// overhead. Copying of overlapping regions is not supported.
void GenerateCopyCharacters(MacroAssembler* masm,
Register dest,
Register src,
Register count,
Register scratch,
bool ascii);
// Generate code for copying characters using the rep movs instruction.
// Copies ecx characters from esi to edi. Copying of overlapping regions is
// not supported.
void GenerateCopyCharactersREP(MacroAssembler* masm,
Register dest, // Must be edi.
Register src, // Must be esi.
Register count, // Must be ecx.
Register scratch, // Neither of the above.
bool ascii);
};
class StringAddStub: public StringStubBase {
public:
explicit StringAddStub(StringAddFlags flags) {
string_check_ = ((flags & NO_STRING_CHECK_IN_STUB) == 0);
@ -759,18 +787,23 @@ class StringAddStub: public CodeStub {
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_;
};
class SubStringStub: public StringStubBase {
public:
SubStringStub() {}
private:
Major MajorKey() { return SubString; }
int MinorKey() { return 0; }
void Generate(MacroAssembler* masm);
};
} } // namespace v8::internal
#endif // V8_IA32_CODEGEN_IA32_H_

View File

@ -2473,6 +2473,7 @@ static Object* Runtime_SubString(Arguments args) {
RUNTIME_ASSERT(end >= start);
RUNTIME_ASSERT(start >= 0);
RUNTIME_ASSERT(end <= value->length());
Counters::sub_string_runtime.Increment();
return value->SubString(start, end);
}

View File

@ -182,7 +182,7 @@ function SubString(string, start, end) {
}
return %CharFromCode(char_code);
}
return %SubString(string, start, end);
return %_SubString(string, start, end);
}

View File

@ -154,6 +154,8 @@ namespace internal {
SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs) \
SC(string_add_runtime, V8.StringAddRuntime) \
SC(string_add_native, V8.StringAddNative) \
SC(sub_string_runtime, V8.SubStringRuntime) \
SC(sub_string_native, V8.SubStringNative) \
SC(regexp_entry_runtime, V8.RegExpEntryRuntime) \
SC(regexp_entry_native, V8.RegExpEntryNative)

View File

@ -3901,6 +3901,18 @@ void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
}
void CodeGenerator::GenerateSubString(ZoneList<Expression*>* args) {
ASSERT_EQ(2, args->length());
Load(args->at(0));
Load(args->at(1));
Load(args->at(2));
Result answer = frame_->CallRuntime(Runtime::kSubString, 3);
frame_->Push(&answer);
}
void CodeGenerator::GenerateClassOf(ZoneList<Expression*>* args) {
ASSERT(args->length() == 1);
JumpTarget leave, null, function, non_function_constructor;

View File

@ -541,6 +541,9 @@ class CodeGenerator: public AstVisitor {
// Fast support for StringAdd.
void GenerateStringAdd(ZoneList<Expression*>* args);
// Fast support for SubString.
void GenerateSubString(ZoneList<Expression*>* args);
// Support for direct calls from JavaScript to native RegExp code.
void GenerateRegExpExec(ZoneList<Expression*>* args);