From 76774115c0003478bf4d4a3c20e341ece9e64a0f Mon Sep 17 00:00:00 2001 From: "lrn@chromium.org" Date: Mon, 25 Jan 2010 08:55:08 +0000 Subject: [PATCH] Ported SubStringStub to X64. Review URL: http://codereview.chromium.org/555049 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3683 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/x64/assembler-x64.cc | 34 +++++++ src/x64/assembler-x64.h | 7 ++ src/x64/codegen-x64.cc | 181 +++++++++++++++++++++++++++++++-- src/x64/codegen-x64.h | 43 ++++++-- src/x64/disasm-x64.cc | 30 +++++- src/x64/macro-assembler-x64.cc | 45 +++++++- src/x64/macro-assembler-x64.h | 16 +++ 7 files changed, 339 insertions(+), 17 deletions(-) diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc index 4ac39339c9..8a3e869de7 100644 --- a/src/x64/assembler-x64.cc +++ b/src/x64/assembler-x64.cc @@ -1537,6 +1537,40 @@ void Assembler::movzxwl(Register dst, const Operand& src) { } +void Assembler::repmovsb() { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0xF3); + emit(0xA4); +} + + +void Assembler::repmovsw() { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0x66); // Operand size override. + emit(0xF3); + emit(0xA4); +} + + +void Assembler::repmovsl() { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0xF3); + emit(0xA5); +} + + +void Assembler::repmovsq() { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0xF3); + emit_rex_64(); + emit(0xA5); +} + + void Assembler::mul(Register src) { EnsureSpace ensure_space(this); last_pc_ = pc_; diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h index 1bddb2fb03..522098a732 100644 --- a/src/x64/assembler-x64.h +++ b/src/x64/assembler-x64.h @@ -574,6 +574,13 @@ class Assembler : public Malloced { void movzxwq(Register dst, const Operand& src); void movzxwl(Register dst, const Operand& src); + // Repeated moves. + + void repmovsb(); + void repmovsw(); + void repmovsl(); + void repmovsq(); + // New x64 instruction to load from an immediate 64-bit pointer into RAX. void load_rax(void* ptr, RelocInfo::Mode rmode); void load_rax(ExternalReference ext); diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index 9a9cc3fb81..1aaac64a5b 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -3942,7 +3942,8 @@ void CodeGenerator::GenerateSubString(ZoneList* args) { Load(args->at(1)); Load(args->at(2)); - Result answer = frame_->CallRuntime(Runtime::kSubString, 3); + SubStringStub stub; + Result answer = frame_->CallStub(&stub, 3); frame_->Push(&answer); } @@ -8138,11 +8139,11 @@ void StringAddStub::Generate(MacroAssembler* masm) { } -void StringAddStub::GenerateCopyCharacters(MacroAssembler* masm, - Register dest, - Register src, - Register count, - bool ascii) { +void StringStubBase::GenerateCopyCharacters(MacroAssembler* masm, + Register dest, + Register src, + Register count, + bool ascii) { Label loop; __ bind(&loop); // This loop just copies one character at a time, as it is only used for very @@ -8163,6 +8164,174 @@ void StringAddStub::GenerateCopyCharacters(MacroAssembler* masm, } +void StringStubBase::GenerateCopyCharactersREP(MacroAssembler* masm, + Register dest, + Register src, + Register count, + 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(rdi)); // rep movs destination + ASSERT(src.is(rsi)); // rep movs source + ASSERT(count.is(rcx)); // rep movs count + + // Nothing to do for zero characters. + Label done; + __ testq(count, count); + __ j(zero, &done); + + // Make count the number of bytes to copy. + if (!ascii) { + ASSERT_EQ(2, sizeof(uc16)); // NOLINT + __ addq(count, count); + } + + // Don't enter the rep movs if there are less than 4 bytes to copy. + Label last_bytes; + __ testq(count, Immediate(~7)); + __ j(zero, &last_bytes); + + // Copy from edi to esi using rep movs instruction. + __ movq(kScratchRegister, count); + __ sar(count, Immediate(3)); // Number of doublewords to copy. + __ repmovsq(); + + // Find number of bytes left. + __ movq(count, kScratchRegister); + __ and_(count, Immediate(7)); + + // Check if there are more bytes to copy. + __ bind(&last_bytes); + __ testq(count, count); + __ j(zero, &done); + + // Copy remaining characters. + Label loop; + __ bind(&loop); + __ movb(kScratchRegister, Operand(src, 0)); + __ movb(Operand(dest, 0), kScratchRegister); + __ addq(src, Immediate(1)); + __ addq(dest, Immediate(1)); + __ subq(count, Immediate(1)); + __ j(not_zero, &loop); + + __ bind(&done); +} + + +void SubStringStub::Generate(MacroAssembler* masm) { + Label runtime; + + // Stack frame on entry. + // rsp[0]: return address + // rsp[8]: to + // rsp[16]: from + // rsp[24]: string + + const int kToOffset = 1 * kPointerSize; + const int kFromOffset = kToOffset + kPointerSize; + const int kStringOffset = kFromOffset + kPointerSize; + const int kArgumentsSize = (kStringOffset + kPointerSize) - kToOffset; + + // Make sure first argument is a string. + __ movq(rax, Operand(rsp, kStringOffset)); + ASSERT_EQ(0, kSmiTag); + __ testl(rax, Immediate(kSmiTagMask)); + __ j(zero, &runtime); + Condition is_string = masm->IsObjectStringType(rax, rbx, rbx); + __ j(NegateCondition(is_string), &runtime); + + // rax: string + // rbx: instance type + // Calculate length of sub string using the smi values. + __ movq(rcx, Operand(rsp, kToOffset)); + __ movq(rdx, Operand(rsp, kFromOffset)); + __ JumpIfNotBothPositiveSmi(rcx, rdx, &runtime); + + __ SmiSub(rcx, rcx, rdx, NULL); // Overflow doesn't happen. + __ j(negative, &runtime); + // Handle sub-strings of length 2 and less in the runtime system. + __ SmiToInteger32(rcx, rcx); + __ cmpl(rcx, Immediate(2)); + __ j(below_equal, &runtime); + + // rax: string + // rbx: instance type + // rcx: result string length + // Check for flat ascii string + Label non_ascii_flat; + __ and_(rbx, Immediate(kStringRepresentationMask | kStringEncodingMask)); + __ cmpb(rbx, Immediate(kSeqStringTag | kAsciiStringTag)); + __ j(not_equal, &non_ascii_flat); + + // Allocate the result. + __ AllocateAsciiString(rax, rcx, rbx, rdx, rdi, &runtime); + + // rax: result string + // rcx: result string length + __ movq(rdx, rsi); // esi used by following code. + // Locate first character of result. + __ lea(rdi, FieldOperand(rax, SeqAsciiString::kHeaderSize)); + // Load string argument and locate character of sub string start. + __ movq(rsi, Operand(rsp, kStringOffset)); + __ movq(rbx, Operand(rsp, kFromOffset)); + { + SmiIndex smi_as_index = masm->SmiToIndex(rbx, rbx, times_1); + __ lea(rsi, Operand(rsi, smi_as_index.reg, smi_as_index.scale, + SeqAsciiString::kHeaderSize - kHeapObjectTag)); + } + + // rax: result string + // rcx: result length + // rdx: original value of rsi + // rdi: first character of result + // rsi: character of sub string start + GenerateCopyCharactersREP(masm, rdi, rsi, rcx, true); + __ movq(rsi, rdx); // Restore rsi. + __ IncrementCounter(&Counters::sub_string_native, 1); + __ ret(kArgumentsSize); + + __ bind(&non_ascii_flat); + // rax: string + // rbx: instance type & kStringRepresentationMask | kStringEncodingMask + // rcx: result string length + // Check for sequential two byte string + __ cmpb(rbx, Immediate(kSeqStringTag | kTwoByteStringTag)); + __ j(not_equal, &runtime); + + // Allocate the result. + __ AllocateTwoByteString(rax, rcx, rbx, rdx, rdi, &runtime); + + // rax: result string + // rcx: result string length + __ movq(rdx, rsi); // esi used by following code. + // Locate first character of result. + __ lea(rdi, FieldOperand(rax, SeqTwoByteString::kHeaderSize)); + // Load string argument and locate character of sub string start. + __ movq(rsi, Operand(rsp, kStringOffset)); + __ movq(rbx, Operand(rsp, kFromOffset)); + { + SmiIndex smi_as_index = masm->SmiToIndex(rbx, rbx, times_2); + __ lea(rsi, Operand(rsi, smi_as_index.reg, smi_as_index.scale, + SeqAsciiString::kHeaderSize - kHeapObjectTag)); + } + + // rax: result string + // rcx: result length + // rdx: original value of rsi + // rdi: first character of result + // rsi: character of sub string start + GenerateCopyCharactersREP(masm, rdi, rsi, rcx, false); + __ movq(rsi, rdx); // Restore esi. + __ IncrementCounter(&Counters::sub_string_native, 1); + __ ret(kArgumentsSize); + + // Just jump to runtime to create the sub string. + __ bind(&runtime); + __ TailCallRuntime(ExternalReference(Runtime::kSubString), 3, 1); +} + void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm, Register left, diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h index 766ca139de..af7c804daf 100644 --- a/src/x64/codegen-x64.h +++ b/src/x64/codegen-x64.h @@ -713,6 +713,29 @@ class GenericBinaryOpStub: 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, + bool ascii); + + // Generate code for copying characters using the rep movs instruction. + // Copies rcx characters from rsi to rdi. Copying of overlapping regions is + // not supported. + void GenerateCopyCharactersREP(MacroAssembler* masm, + Register dest, // Must be rdi. + Register src, // Must be rsi. + Register count, // Must be rcx. + bool ascii); +}; + + // Flag that indicates how to generate code for the stub StringAddStub. enum StringAddFlags { NO_STRING_ADD_FLAGS = 0, @@ -720,7 +743,7 @@ enum StringAddFlags { }; -class StringAddStub: public CodeStub { +class StringAddStub: public StringStubBase { public: explicit StringAddStub(StringAddFlags flags) { string_check_ = ((flags & NO_STRING_CHECK_IN_STUB) == 0); @@ -732,17 +755,23 @@ class StringAddStub: public CodeStub { void Generate(MacroAssembler* masm); - void GenerateCopyCharacters(MacroAssembler* masm, - Register desc, - Register src, - Register count, - 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); +}; + + class StringCompareStub: public CodeStub { public: explicit StringCompareStub() {} diff --git a/src/x64/disasm-x64.cc b/src/x64/disasm-x64.cc index 0b43e766e6..ce3aae8a2c 100644 --- a/src/x64/disasm-x64.cc +++ b/src/x64/disasm-x64.cc @@ -114,6 +114,10 @@ static ByteMnemonic zero_operands_instr[] = { { 0x9E, UNSET_OP_ORDER, "sahf" }, { 0x99, UNSET_OP_ORDER, "cdq" }, { 0x9B, UNSET_OP_ORDER, "fwait" }, + { 0xA4, UNSET_OP_ORDER, "movs" }, + { 0xA5, UNSET_OP_ORDER, "movs" }, + { 0xA6, UNSET_OP_ORDER, "cmps" }, + { 0xA7, UNSET_OP_ORDER, "cmps" }, { -1, UNSET_OP_ORDER, "" } }; @@ -157,6 +161,16 @@ enum InstructionType { }; +enum Prefixes { + ESCAPE_PREFIX = 0x0F, + OPERAND_SIZE_OVERRIDE_PREFIX = 0x66, + ADDRESS_SIZE_OVERRIDE_PREFIX = 0x67, + REPNE_PREFIX = 0xF2, + REP_PREFIX = 0xF3, + REPEQ_PREFIX = REP_PREFIX +}; + + struct InstructionDesc { const char* mnem; InstructionType type; @@ -1128,12 +1142,12 @@ int DisassemblerX64::InstructionDecode(v8::internal::Vector out_buffer, // Scan for prefixes. while (true) { current = *data; - if (current == 0x66) { // Group 3 prefix. + if (current == OPERAND_SIZE_OVERRIDE_PREFIX) { // Group 3 prefix. operand_size_ = current; } else if ((current & 0xF0) == 0x40) { // REX prefix. setRex(current); if (rex_w()) AppendToBuffer("REX.W "); - } else if ((current & 0xFE) == 0xF2) { // Group 1 prefix. + } else if ((current & 0xFE) == 0xF2) { // Group 1 prefix (0xF2 or 0xF3). group_1_prefix_ = current; } else { // Not a prefix - an opcode. break; @@ -1145,7 +1159,17 @@ int DisassemblerX64::InstructionDecode(v8::internal::Vector out_buffer, byte_size_operand_ = idesc.byte_size_operation; switch (idesc.type) { case ZERO_OPERANDS_INSTR: - AppendToBuffer(idesc.mnem); + if (current >= 0xA4 && current <= 0xA7) { + // String move or compare operations. + if (group_1_prefix_ == REP_PREFIX) { + // REP. + AppendToBuffer("rep "); + } + if (rex_w()) AppendToBuffer("REX.W "); + AppendToBuffer("%s%c", idesc.mnem, operand_size_code()); + } else { + AppendToBuffer("%s", idesc.mnem, operand_size_code()); + } data++; break; diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index a0ebfdc95d..b06b8c8a9d 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -581,6 +581,20 @@ Condition MacroAssembler::CheckBothSmi(Register first, Register second) { } +Condition MacroAssembler::CheckBothPositiveSmi(Register first, + Register second) { + if (first.is(second)) { + return CheckPositiveSmi(first); + } + movl(kScratchRegister, first); + orl(kScratchRegister, second); + rol(kScratchRegister, Immediate(1)); + testl(kScratchRegister, Immediate(0x03)); + return zero; +} + + + Condition MacroAssembler::CheckEitherSmi(Register first, Register second) { if (first.is(second)) { return CheckSmi(first); @@ -660,7 +674,17 @@ void MacroAssembler::SmiSub(Register dst, Register src2, Label* on_not_smi_result) { ASSERT(!dst.is(src2)); - if (dst.is(src1)) { + if (on_not_smi_result == NULL) { + // No overflow checking. Use only when it's known that + // overflowing is impossible (e.g., subtracting two positive smis). + if (dst.is(src1)) { + subq(dst, src2); + } else { + movq(dst, src1); + subq(dst, src2); + } + Assert(no_overflow, "Smi substraction onverflow"); + } else if (dst.is(src1)) { subq(dst, src2); Label smi_result; j(no_overflow, &smi_result); @@ -1292,6 +1316,14 @@ void MacroAssembler::JumpIfNotBothSmi(Register src1, Register src2, } +void MacroAssembler::JumpIfNotBothPositiveSmi(Register src1, Register src2, + Label* on_not_both_smi) { + Condition both_smi = CheckBothPositiveSmi(src1, src2); + j(NegateCondition(both_smi), on_not_both_smi); +} + + + void MacroAssembler::JumpIfNotBothSequentialAsciiStrings(Register first_object, Register second_object, Register scratch1, @@ -1517,6 +1549,17 @@ void MacroAssembler::CmpInstanceType(Register map, InstanceType type) { } +Condition MacroAssembler::IsObjectStringType(Register heap_object, + Register map, + Register instance_type) { + movq(map, FieldOperand(heap_object, HeapObject::kMapOffset)); + movzxbq(instance_type, FieldOperand(map, Map::kInstanceTypeOffset)); + ASSERT(kNotStringTag != 0); + testb(instance_type, Immediate(kIsNotStringMask)); + return zero; +} + + void MacroAssembler::TryGetFunctionPrototype(Register function, Register result, Label* miss) { diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h index ce2848c059..8d4a8f2e2a 100644 --- a/src/x64/macro-assembler-x64.h +++ b/src/x64/macro-assembler-x64.h @@ -207,6 +207,9 @@ class MacroAssembler: public Assembler { // Are both values tagged smis. Condition CheckBothSmi(Register first, Register second); + // Are both values tagged smis. + Condition CheckBothPositiveSmi(Register first, Register second); + // Are either value a tagged smi. Condition CheckEitherSmi(Register first, Register second); @@ -248,6 +251,10 @@ class MacroAssembler: public Assembler { // Jump if either or both register are not smi values. void JumpIfNotBothSmi(Register src1, Register src2, Label* on_not_both_smi); + // Jump if either or both register are not positive smi values. + void JumpIfNotBothPositiveSmi(Register src1, Register src2, + Label* on_not_both_smi); + // Operations on tagged smi values. // Smis represent a subset of integers. The subset is always equivalent to @@ -452,6 +459,15 @@ class MacroAssembler: public Assembler { // Always use unsigned comparisons: above and below, not less and greater. void CmpInstanceType(Register map, InstanceType type); + // Check if the object in register heap_object is a string. Afterwards the + // register map contains the object map and the register instance_type + // contains the instance_type. The registers map and instance_type can be the + // same in which case it contains the instance type afterwards. Either of the + // registers map and instance_type can be the same as heap_object. + Condition IsObjectStringType(Register heap_object, + Register map, + Register instance_type); + // FCmp is similar to integer cmp, but requires unsigned // jcc instructions (je, ja, jae, jb, jbe, je, and jz). void FCmp();