From b8f0b88b54ba913327862873bec48f466da3924b Mon Sep 17 00:00:00 2001 From: "ager@chromium.org" Date: Fri, 18 Feb 2011 14:00:46 +0000 Subject: [PATCH] x64: Implement regexp literals and string CharCodeAt in lithium backend. Review URL: http://codereview.chromium.org/6469052 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6853 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/ia32/assembler-ia32.h | 2 +- src/ia32/lithium-codegen-ia32.cc | 9 +- src/x64/assembler-x64.cc | 15 ++-- src/x64/assembler-x64.h | 13 ++- src/x64/lithium-codegen-x64.cc | 190 ++++++++++++++++++++++++++++++++++++++- src/x64/lithium-codegen-x64.h | 1 + src/x64/lithium-x64.cc | 9 +- src/x64/lithium-x64.h | 16 ++++ 8 files changed, 236 insertions(+), 19 deletions(-) diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h index 568b4d8..91bcb57 100644 --- a/src/ia32/assembler-ia32.h +++ b/src/ia32/assembler-ia32.h @@ -184,7 +184,7 @@ typedef XMMRegister DoubleRegister; // Index of register used in pusha/popa. -// Order of pushed registers: EAX, ECX, EDX, EBX, ESP, EBP, ESI, and EDI +// Order of pushed registers: eax, ecx, edx, ebx, esp, ebp, esi, and edi. inline int EspIndexForPushAll(Register reg) { return Register::kNumRegisters - 1 - reg.code(); } diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index ca72c97..2bf9395 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -2859,19 +2859,20 @@ void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) { __ test(result, Immediate(kStringRepresentationMask)); __ j(not_zero, deferred->entry()); - // Check for 1-byte or 2-byte string. + // Check for ASCII or two-byte string. __ bind(&flat_string); STATIC_ASSERT(kAsciiStringTag != 0); __ test(result, Immediate(kStringEncodingMask)); __ j(not_zero, &ascii_string); - // 2-byte string. - // Load the 2-byte character code into the result register. + // Two-byte string. + // Load the two-byte character code into the result register. STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1); if (instr->index()->IsConstantOperand()) { __ movzx_w(result, FieldOperand(string, - SeqTwoByteString::kHeaderSize + 2 * const_index)); + SeqTwoByteString::kHeaderSize + + (kUC16Size * const_index))); } else { __ movzx_w(result, FieldOperand(string, index, diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc index 697f6cd..1c5b103 100644 --- a/src/x64/assembler-x64.cc +++ b/src/x64/assembler-x64.cc @@ -190,13 +190,18 @@ void RelocInfo::PatchCode(byte* instructions, int instruction_count) { // ----------------------------------------------------------------------------- // Register constants. -const int Register::registerCodeByAllocationIndex[kNumAllocatableRegisters] = { - // rax, rbx, rdx, rcx, rdi, r8, r9, r11, r14, r12 - 0, 3, 2, 1, 7, 8, 9, 11, 14, 12 +const int Register::kRegisterCodeByAllocationIndex[kNumAllocatableRegisters] = { + // rax, rbx, rdx, rcx, rdi, r8, r9, r11, r14, r12 + 0, 3, 2, 1, 7, 8, 9, 11, 14, 12 }; -const int Register::allocationIndexByRegisterCode[kNumRegisters] = { - 0, 3, 2, 1, -1, -1, -1, 4, 5, 6, -1, 7, 9, -1, 8, -1 +const int Register::kAllocationIndexByRegisterCode[kNumRegisters] = { + 0, 3, 2, 1, -1, -1, -1, 4, 5, 6, -1, 7, 9, -1, 8, -1 +}; + + +const int Register::kRspIndexForPushAllByRegisterCode[kNumRegisters] = { + 10, 9, 8, 7, -1, -1, 6, 5, 4, 3, -1, 2, 1, -1, 0, -1 }; diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h index 91e7e6c..6e1837d 100644 --- a/src/x64/assembler-x64.h +++ b/src/x64/assembler-x64.h @@ -99,15 +99,19 @@ struct Register { static const int kNumAllocatableRegisters = 10; static int ToAllocationIndex(Register reg) { - return allocationIndexByRegisterCode[reg.code()]; + return kAllocationIndexByRegisterCode[reg.code()]; } static Register FromAllocationIndex(int index) { ASSERT(index >= 0 && index < kNumAllocatableRegisters); - Register result = { registerCodeByAllocationIndex[index] }; + Register result = { kRegisterCodeByAllocationIndex[index] }; return result; } + static int ToRspIndexForPushAll(Register reg) { + return kRspIndexForPushAllByRegisterCode[reg.code()]; + } + static const char* AllocationIndexToString(int index) { ASSERT(index >= 0 && index < kNumAllocatableRegisters); const char* const names[] = { @@ -155,8 +159,9 @@ struct Register { int code_; private: - static const int registerCodeByAllocationIndex[kNumAllocatableRegisters]; - static const int allocationIndexByRegisterCode[kNumRegisters]; + static const int kRegisterCodeByAllocationIndex[kNumAllocatableRegisters]; + static const int kAllocationIndexByRegisterCode[kNumRegisters]; + static const int kRspIndexForPushAllByRegisterCode[kNumRegisters]; }; const Register rax = { 0 }; diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index f8204c5..2eea10a 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -2309,6 +2309,147 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { } +void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) { + class DeferredStringCharCodeAt: public LDeferredCode { + public: + DeferredStringCharCodeAt(LCodeGen* codegen, LStringCharCodeAt* instr) + : LDeferredCode(codegen), instr_(instr) { } + virtual void Generate() { codegen()->DoDeferredStringCharCodeAt(instr_); } + private: + LStringCharCodeAt* instr_; + }; + + Register string = ToRegister(instr->string()); + Register index = no_reg; + int const_index = -1; + if (instr->index()->IsConstantOperand()) { + const_index = ToInteger32(LConstantOperand::cast(instr->index())); + STATIC_ASSERT(String::kMaxLength <= Smi::kMaxValue); + if (!Smi::IsValid(const_index)) { + // Guaranteed to be out of bounds because of the assert above. + // So the bounds check that must dominate this instruction must + // have deoptimized already. + if (FLAG_debug_code) { + __ Abort("StringCharCodeAt: out of bounds index."); + } + // No code needs to be generated. + return; + } + } else { + index = ToRegister(instr->index()); + } + Register result = ToRegister(instr->result()); + + DeferredStringCharCodeAt* deferred = + new DeferredStringCharCodeAt(this, instr); + + NearLabel flat_string, ascii_string, done; + + // Fetch the instance type of the receiver into result register. + __ movq(result, FieldOperand(string, HeapObject::kMapOffset)); + __ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset)); + + // We need special handling for non-sequential strings. + STATIC_ASSERT(kSeqStringTag == 0); + __ testb(result, Immediate(kStringRepresentationMask)); + __ j(zero, &flat_string); + + // Handle cons strings and go to deferred code for the rest. + __ testb(result, Immediate(kIsConsStringMask)); + __ j(zero, deferred->entry()); + + // ConsString. + // Check whether the right hand side is the empty string (i.e. if + // this is really a flat string in a cons string). If that is not + // the case we would rather go to the runtime system now to flatten + // the string. + __ CompareRoot(FieldOperand(string, ConsString::kSecondOffset), + Heap::kEmptyStringRootIndex); + __ j(not_equal, deferred->entry()); + // Get the first of the two strings and load its instance type. + __ movq(string, FieldOperand(string, ConsString::kFirstOffset)); + __ movq(result, FieldOperand(string, HeapObject::kMapOffset)); + __ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset)); + // If the first cons component is also non-flat, then go to runtime. + STATIC_ASSERT(kSeqStringTag == 0); + __ testb(result, Immediate(kStringRepresentationMask)); + __ j(not_zero, deferred->entry()); + + // Check for ASCII or two-byte string. + __ bind(&flat_string); + STATIC_ASSERT(kAsciiStringTag != 0); + __ testb(result, Immediate(kStringEncodingMask)); + __ j(not_zero, &ascii_string); + + // Two-byte string. + // Load the two-byte character code into the result register. + STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1); + if (instr->index()->IsConstantOperand()) { + __ movzxwl(result, + FieldOperand(string, + SeqTwoByteString::kHeaderSize + + (kUC16Size * const_index))); + } else { + __ movzxwl(result, FieldOperand(string, + index, + times_2, + SeqTwoByteString::kHeaderSize)); + } + __ jmp(&done); + + // ASCII string. + // Load the byte into the result register. + __ bind(&ascii_string); + if (instr->index()->IsConstantOperand()) { + __ movzxbl(result, FieldOperand(string, + SeqAsciiString::kHeaderSize + const_index)); + } else { + __ movzxbl(result, FieldOperand(string, + index, + times_1, + SeqAsciiString::kHeaderSize)); + } + __ bind(&done); + __ bind(deferred->exit()); +} + + +void LCodeGen::DoDeferredStringCharCodeAt(LStringCharCodeAt* instr) { + Register string = ToRegister(instr->string()); + Register result = ToRegister(instr->result()); + + // TODO(3095996): Get rid of this. For now, we need to make the + // result register contain a valid pointer because it is already + // contained in the register pointer map. + __ Set(result, 0); + + __ PushSafepointRegisters(); + __ push(string); + // Push the index as a smi. This is safe because of the checks in + // DoStringCharCodeAt above. + STATIC_ASSERT(String::kMaxLength <= Smi::kMaxValue); + if (instr->index()->IsConstantOperand()) { + int const_index = ToInteger32(LConstantOperand::cast(instr->index())); + __ Push(Smi::FromInt(const_index)); + } else { + Register index = ToRegister(instr->index()); + __ Integer32ToSmi(index, index); + __ push(index); + } + __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); + __ CallRuntimeSaveDoubles(Runtime::kStringCharCodeAt); + RecordSafepointWithRegisters( + instr->pointer_map(), 2, Safepoint::kNoDeoptimizationIndex); + if (FLAG_debug_code) { + __ AbortIfNotSmi(rax); + } + __ SmiToInteger32(rax, rax); + __ movq(Operand(rsp, Register::ToRspIndexForPushAll(result) * kPointerSize), + rax); + __ PopSafepointRegisters(); +} + + void LCodeGen::DoStringLength(LStringLength* instr) { Register string = ToRegister(instr->string()); Register result = ToRegister(instr->result()); @@ -2667,7 +2808,54 @@ void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) { void LCodeGen::DoRegExpLiteral(LRegExpLiteral* instr) { - Abort("Unimplemented: %s", "DoRegExpLiteral"); + NearLabel materialized; + // Registers will be used as follows: + // rdi = JS function. + // rcx = literals array. + // rbx = regexp literal. + // rax = regexp literal clone. + __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); + __ movq(rcx, FieldOperand(rdi, JSFunction::kLiteralsOffset)); + int literal_offset = FixedArray::kHeaderSize + + instr->hydrogen()->literal_index() * kPointerSize; + __ movq(rbx, FieldOperand(rcx, literal_offset)); + __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex); + __ j(not_equal, &materialized); + + // Create regexp literal using runtime function + // Result will be in rax. + __ push(rcx); + __ Push(Smi::FromInt(instr->hydrogen()->literal_index())); + __ Push(instr->hydrogen()->pattern()); + __ Push(instr->hydrogen()->flags()); + CallRuntime(Runtime::kMaterializeRegExpLiteral, 4, instr); + __ movq(rbx, rax); + + __ bind(&materialized); + int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize; + Label allocated, runtime_allocate; + __ AllocateInNewSpace(size, rax, rcx, rdx, &runtime_allocate, TAG_OBJECT); + __ jmp(&allocated); + + __ bind(&runtime_allocate); + __ push(rbx); + __ Push(Smi::FromInt(size)); + CallRuntime(Runtime::kAllocateInNewSpace, 1, instr); + __ pop(rbx); + + __ bind(&allocated); + // Copy the content into the newly allocated memory. + // (Unroll copy loop once for better throughput). + for (int i = 0; i < size - kPointerSize; i += 2 * kPointerSize) { + __ movq(rdx, FieldOperand(rbx, i)); + __ movq(rcx, FieldOperand(rbx, i + kPointerSize)); + __ movq(FieldOperand(rax, i), rdx); + __ movq(FieldOperand(rax, i + kPointerSize), rcx); + } + if ((size % (2 * kPointerSize)) != 0) { + __ movq(rdx, FieldOperand(rbx, size - kPointerSize)); + __ movq(FieldOperand(rax, size - kPointerSize), rdx); + } } diff --git a/src/x64/lithium-codegen-x64.h b/src/x64/lithium-codegen-x64.h index 57db0ed..6b43057 100644 --- a/src/x64/lithium-codegen-x64.h +++ b/src/x64/lithium-codegen-x64.h @@ -90,6 +90,7 @@ class LCodeGen BASE_EMBEDDED { void DoDeferredTaggedToI(LTaggedToI* instr); void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr); void DoDeferredStackCheck(LGoto* instr); + void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr); void DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr); // Parallel move support. diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index c2ede2c..b328726 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -1782,8 +1782,10 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) { LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) { - Abort("Unimplemented: %s", "DoStringCharCodeAt"); - return NULL; + LOperand* string = UseRegister(instr->string()); + LOperand* index = UseRegisterOrConstant(instr->index()); + LStringCharCodeAt* result = new LStringCharCodeAt(string, index); + return AssignEnvironment(AssignPointerMap(DefineAsRegister(result))); } @@ -1804,8 +1806,7 @@ LInstruction* LChunkBuilder::DoObjectLiteral(HObjectLiteral* instr) { LInstruction* LChunkBuilder::DoRegExpLiteral(HRegExpLiteral* instr) { - Abort("Unimplemented: %s", "DoRegExpLiteral"); - return NULL; + return MarkAsCall(DefineFixed(new LRegExpLiteral, rax), instr); } diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h index 66df23f..e2ee325 100644 --- a/src/x64/lithium-x64.h +++ b/src/x64/lithium-x64.h @@ -147,6 +147,7 @@ class LCodeGen; V(StoreNamedField) \ V(StoreNamedGeneric) \ V(StorePixelArrayElement) \ + V(StringCharCodeAt) \ V(StringLength) \ V(SubI) \ V(TaggedToI) \ @@ -1572,6 +1573,21 @@ class LStoreKeyedGeneric: public LStoreKeyed { }; +class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> { + public: + LStringCharCodeAt(LOperand* string, LOperand* index) { + inputs_[0] = string; + inputs_[1] = index; + } + + DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt, "string-char-code-at") + DECLARE_HYDROGEN_ACCESSOR(StringCharCodeAt) + + LOperand* string() { return inputs_[0]; } + LOperand* index() { return inputs_[1]; } +}; + + class LStringLength: public LTemplateInstruction<1, 1, 0> { public: explicit LStringLength(LOperand* string) { -- 2.7.4