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:
parent
060711ab37
commit
3d36c712aa
@ -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());
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -37,6 +37,7 @@ namespace internal {
|
||||
V(CallFunction) \
|
||||
V(GenericBinaryOp) \
|
||||
V(StringAdd) \
|
||||
V(SubString) \
|
||||
V(SmiOp) \
|
||||
V(Compare) \
|
||||
V(RecordWrite) \
|
||||
|
@ -345,6 +345,7 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = {
|
||||
{&CodeGenerator::GenerateIsObject, "_IsObject"},
|
||||
{&CodeGenerator::GenerateIsFunction, "_IsFunction"},
|
||||
{&CodeGenerator::GenerateStringAdd, "_StringAdd"},
|
||||
{&CodeGenerator::GenerateSubString, "_SubString"},
|
||||
{&CodeGenerator::GenerateRegExpExec, "_RegExpExec"},
|
||||
};
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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_
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -182,7 +182,7 @@ function SubString(string, start, end) {
|
||||
}
|
||||
return %CharFromCode(char_code);
|
||||
}
|
||||
return %SubString(string, start, end);
|
||||
return %_SubString(string, start, end);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user