From: vegorov@chromium.org Date: Mon, 15 Mar 2010 17:11:08 +0000 (+0000) Subject: Port of string plus smi optimization from ia32 to x64 and ARM. X-Git-Tag: upstream/4.7.83~22246 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ed5326800685cef2126b3ffa634480eb5f763df5;p=platform%2Fupstream%2Fv8.git Port of string plus smi optimization from ia32 to x64 and ARM. Review URL: http://codereview.chromium.org/668254 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4138 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index 3bf3fb8..d3e98a3 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -3681,7 +3681,8 @@ void CodeGenerator::GenerateNumberToString(ZoneList* args) { // Load the argument on the stack and jump to the runtime. Load(args->at(0)); - frame_->CallRuntime(Runtime::kNumberToString, 1); + NumberToStringStub stub; + frame_->CallStub(&stub, 1); frame_->EmitPush(r0); } @@ -5280,6 +5281,79 @@ static void EmitCheckForSymbols(MacroAssembler* masm, Label* slow) { } +void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm, + Register object, + Register result, + Register scratch1, + Register scratch2, + bool object_is_smi, + Label* not_found) { + // Currently only lookup for smis. Check for smi if object is not known to be + // a smi. + if (!object_is_smi) { + ASSERT(kSmiTag == 0); + __ tst(object, Operand(kSmiTagMask)); + __ b(ne, not_found); + } + + // Use of registers. Register result is used as a temporary. + Register number_string_cache = result; + Register mask = scratch1; + Register scratch = scratch2; + + // Load the number string cache. + __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex); + + // Make the hash mask from the length of the number string cache. It + // contains two elements (number and string) for each cache entry. + __ ldr(mask, FieldMemOperand(number_string_cache, FixedArray::kLengthOffset)); + // Divide length by two (length is not a smi). + __ mov(mask, Operand(mask, ASR, 1)); + __ sub(mask, mask, Operand(1)); // Make mask. + + // Calculate the entry in the number string cache. The hash value in the + // number string cache for smis is just the smi value. + __ and_(scratch, mask, Operand(object, ASR, 1)); + + // Calculate address of entry in string cache: each entry consists + // of two pointer sized fields. + __ add(scratch, + number_string_cache, + Operand(scratch, LSL, kPointerSizeLog2 + 1)); + + // Check if the entry is the smi we are looking for. + Register object1 = scratch1; + __ ldr(object1, FieldMemOperand(scratch, FixedArray::kHeaderSize)); + __ cmp(object, object1); + __ b(ne, not_found); + + // Get the result from the cache. + __ ldr(result, + FieldMemOperand(scratch, FixedArray::kHeaderSize + kPointerSize)); + + __ IncrementCounter(&Counters::number_to_string_native, + 1, + scratch1, + scratch2); +} + + +void NumberToStringStub::Generate(MacroAssembler* masm) { + Label runtime; + + __ ldr(r1, MemOperand(sp, 0)); + + // Generate code to lookup number in the number string cache. + GenerateLookupNumberStringCache(masm, r1, r0, r2, r3, false, &runtime); + __ add(sp, sp, Operand(1 * kPointerSize)); + __ Ret(); + + __ bind(&runtime); + // Handle number to string in the runtime system if not found in the cache. + __ TailCallRuntime(Runtime::kNumberToString, 1, 1); +} + + // On entry r0 (rhs) and r1 (lhs) are the values to be compared. // On exit r0 is 0, positive or negative to indicate the result of // the comparison. @@ -5503,7 +5577,7 @@ static void HandleBinaryOpSlowCases(MacroAssembler* masm, // sp[0] : second argument // sp[4] : first argument - Label not_strings, not_string1, string1; + Label not_strings, not_string1, string1, string1_smi2; __ tst(r1, Operand(kSmiTagMask)); __ b(eq, ¬_string1); __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE); @@ -5511,13 +5585,24 @@ static void HandleBinaryOpSlowCases(MacroAssembler* masm, // First argument is a a string, test second. __ tst(r0, Operand(kSmiTagMask)); - __ b(eq, &string1); + __ b(eq, &string1_smi2); __ CompareObjectType(r0, r2, r2, FIRST_NONSTRING_TYPE); __ b(ge, &string1); // First and second argument are strings. - StringAddStub stub(NO_STRING_CHECK_IN_STUB); - __ TailCallStub(&stub); + StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB); + __ TailCallStub(&string_add_stub); + + __ bind(&string1_smi2); + // First argument is a string, second is a smi. Try to lookup the number + // string for the smi in the number string cache. + NumberToStringStub::GenerateLookupNumberStringCache( + masm, r0, r2, r4, r5, true, &string1); + + // Replace second argument on stack and tailcall string add stub to make + // the result. + __ str(r2, MemOperand(sp, 0)); + __ TailCallStub(&string_add_stub); // Only first argument is a string. __ bind(&string1); diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h index 9ade70a..68f293a 100644 --- a/src/arm/codegen-arm.h +++ b/src/arm/codegen-arm.h @@ -660,6 +660,39 @@ class StringCompareStub: public CodeStub { }; +class NumberToStringStub: public CodeStub { + public: + NumberToStringStub() { } + + // Generate code to do a lookup in the number string cache. If the number in + // the register object is found in the cache the generated code falls through + // with the result in the result register. The object and the result register + // can be the same. If the number is not found in the cache the code jumps to + // the label not_found with only the content of register object unchanged. + static void GenerateLookupNumberStringCache(MacroAssembler* masm, + Register object, + Register result, + Register scratch1, + Register scratch2, + bool object_is_smi, + Label* not_found); + + private: + Major MajorKey() { return NumberToString; } + int MinorKey() { return 0; } + + void Generate(MacroAssembler* masm); + + const char* GetName() { return "NumberToStringStub"; } + +#ifdef DEBUG + void Print() { + PrintF("NumberToStringStub\n"); + } +#endif +}; + + } } // namespace v8::internal #endif // V8_ARM_CODEGEN_ARM_H_ diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index f5a8d3c..8ec1c95 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -4053,8 +4053,9 @@ void CodeGenerator::GenerateNumberToString(ZoneList* args) { // Load the argument on the stack and jump to the runtime. Load(args->at(0)); - Result answer = frame_->CallRuntime(Runtime::kNumberToString, 1); - frame_->Push(&answer); + NumberToStringStub stub; + Result result = frame_->CallStub(&stub, 1); + frame_->Push(&result); } @@ -7207,6 +7208,77 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { } +void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm, + Register object, + Register result, + Register scratch1, + Register scratch2, + bool object_is_smi, + Label* not_found) { + // Currently only lookup for smis. Check for smi if object is not known to be + // a smi. + if (!object_is_smi) { + __ JumpIfNotSmi(object, not_found); + } + + // Use of registers. Register result is used as a temporary. + Register number_string_cache = result; + Register mask = scratch1; + Register scratch = scratch2; + + // Load the number string cache. + __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex); + + // Make the hash mask from the length of the number string cache. It + // contains two elements (number and string) for each cache entry. + __ movl(mask, FieldOperand(number_string_cache, FixedArray::kLengthOffset)); + __ shrl(mask, Immediate(1)); // Divide length by two (length is not a smi). + __ subl(mask, Immediate(1)); // Make mask. + + // Calculate the entry in the number string cache. The hash value in the + // number string cache for smis is just the smi value. + __ movq(scratch, object); + __ SmiToInteger32(scratch, scratch); + __ andl(scratch, mask); + + // Each entry in string cache consists of two pointer sized fields, + // but times_twice_pointer_size (multiplication by 16) scale factor + // is not supported by addrmode on x64 platform. + // So we have to premultiply entry index before lookup + __ shl(scratch, Immediate(kPointerSizeLog2 + 1)); + // Check if the entry is the smi we are looking for. + __ cmpq(object, + FieldOperand(number_string_cache, + scratch, + times_1, + FixedArray::kHeaderSize)); + __ j(not_equal, not_found); + + // Get the result from the cache. + __ movq(result, + FieldOperand(number_string_cache, + scratch, + times_1, + FixedArray::kHeaderSize + kPointerSize)); + __ IncrementCounter(&Counters::number_to_string_native, 1); +} + + +void NumberToStringStub::Generate(MacroAssembler* masm) { + Label runtime; + + __ movq(rbx, Operand(rsp, kPointerSize)); + + // Generate code to lookup number in the number string cache. + GenerateLookupNumberStringCache(masm, rbx, rax, r8, r9, false, &runtime); + __ ret(1 * kPointerSize); + + __ bind(&runtime); + // Handle number to string in the runtime system if not found in the cache. + __ TailCallRuntime(Runtime::kNumberToString, 1, 1); +} + + void CompareStub::Generate(MacroAssembler* masm) { Label call_builtin, done; @@ -8794,6 +8866,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { // result. If arguments was passed in registers now place them on the // stack in the correct order below the return address. __ bind(&call_runtime); + if (HasArgsInRegisters()) { __ pop(rcx); if (HasArgsReversed()) { @@ -8805,48 +8878,63 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { } __ push(rcx); } + switch (op_) { case Token::ADD: { + // Registers containing left and right operands respectively. + Register lhs, rhs; + + if (HasArgsReversed()) { + lhs = rax; + rhs = rdx; + } else { + lhs = rdx; + rhs = rax; + } + // Test for string arguments before calling runtime. - Label not_strings, both_strings, not_string1, string1; + Label not_strings, both_strings, not_string1, string1, string1_smi2; Condition is_smi; Result answer; - is_smi = masm->CheckSmi(rdx); + is_smi = masm->CheckSmi(lhs); __ j(is_smi, ¬_string1); - __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rdx); + __ CmpObjectType(lhs, FIRST_NONSTRING_TYPE, r8); __ j(above_equal, ¬_string1); // First argument is a a string, test second. - is_smi = masm->CheckSmi(rax); - __ j(is_smi, &string1); - __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, rax); + is_smi = masm->CheckSmi(rhs); + __ j(is_smi, &string1_smi2); + __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, r9); __ j(above_equal, &string1); // First and second argument are strings. - StringAddStub stub(NO_STRING_CHECK_IN_STUB); - __ TailCallStub(&stub); + StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB); + __ TailCallStub(&string_add_stub); + + __ bind(&string1_smi2); + // First argument is a string, second is a smi. Try to lookup the number + // string for the smi in the number string cache. + NumberToStringStub::GenerateLookupNumberStringCache( + masm, rhs, rbx, rcx, r8, true, &string1); + + // Replace second argument on stack and tailcall string add stub to make + // the result. + __ movq(Operand(rsp, 1 * kPointerSize), rbx); + __ TailCallStub(&string_add_stub); // Only first argument is a string. __ bind(&string1); - __ InvokeBuiltin( - HasArgsReversed() ? - Builtins::STRING_ADD_RIGHT : - Builtins::STRING_ADD_LEFT, - JUMP_FUNCTION); + __ InvokeBuiltin(Builtins::STRING_ADD_LEFT, JUMP_FUNCTION); // First argument was not a string, test second. __ bind(¬_string1); - is_smi = masm->CheckSmi(rax); + is_smi = masm->CheckSmi(rhs); __ j(is_smi, ¬_strings); - __ CmpObjectType(rax, FIRST_NONSTRING_TYPE, rax); + __ CmpObjectType(rhs, FIRST_NONSTRING_TYPE, rhs); __ j(above_equal, ¬_strings); // Only second argument is a string. - __ InvokeBuiltin( - HasArgsReversed() ? - Builtins::STRING_ADD_LEFT : - Builtins::STRING_ADD_RIGHT, - JUMP_FUNCTION); + __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_FUNCTION); __ bind(¬_strings); // Neither argument is a string. diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h index 3256cb2..ce5f6d1 100644 --- a/src/x64/codegen-x64.h +++ b/src/x64/codegen-x64.h @@ -865,6 +865,39 @@ class StringCompareStub: public CodeStub { }; +class NumberToStringStub: public CodeStub { + public: + NumberToStringStub() { } + + // Generate code to do a lookup in the number string cache. If the number in + // the register object is found in the cache the generated code falls through + // with the result in the result register. The object and the result register + // can be the same. If the number is not found in the cache the code jumps to + // the label not_found with only the content of register object unchanged. + static void GenerateLookupNumberStringCache(MacroAssembler* masm, + Register object, + Register result, + Register scratch1, + Register scratch2, + bool object_is_smi, + Label* not_found); + + private: + Major MajorKey() { return NumberToString; } + int MinorKey() { return 0; } + + void Generate(MacroAssembler* masm); + + const char* GetName() { return "NumberToStringStub"; } + +#ifdef DEBUG + void Print() { + PrintF("NumberToStringStub\n"); + } +#endif +}; + + } } // namespace v8::internal #endif // V8_X64_CODEGEN_X64_H_