diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index 35dc7fe91a..4d9dc52f7f 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -3592,6 +3592,17 @@ void CodeGenerator::GenerateRegExpExec(ZoneList* args) { } +void CodeGenerator::GenerateNumberToString(ZoneList* args) { + ASSERT_EQ(args->length(), 1); + + // Load the argument on the stack and jump to the runtime. + Load(args->at(0)); + + frame_->CallRuntime(Runtime::kNumberToString, 1); + frame_->EmitPush(r0); +} + + void CodeGenerator::GenerateObjectEquals(ZoneList* args) { VirtualFrame::SpilledScope spilled_scope; ASSERT(args->length() == 2); diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h index 2578a398c7..10e28f4110 100644 --- a/src/arm/codegen-arm.h +++ b/src/arm/codegen-arm.h @@ -398,6 +398,9 @@ class CodeGenerator: public AstVisitor { // Support for direct calls from JavaScript to native RegExp code. void GenerateRegExpExec(ZoneList* args); + // Fast support for number to string. + void GenerateNumberToString(ZoneList* args); + // Simple condition analysis. enum ConditionAnalysis { ALWAYS_TRUE, diff --git a/src/code-stubs.h b/src/code-stubs.h index d502f14cd2..3901a64789 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -55,6 +55,7 @@ namespace internal { V(CounterOp) \ V(ArgumentsAccess) \ V(RegExpExec) \ + V(NumberToString) \ V(CEntry) \ V(JSEntry) \ V(DebuggerStatement) diff --git a/src/codegen.cc b/src/codegen.cc index 438ec1e2dc..1c3ecc4059 100644 --- a/src/codegen.cc +++ b/src/codegen.cc @@ -372,6 +372,7 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = { {&CodeGenerator::GenerateSubString, "_SubString"}, {&CodeGenerator::GenerateStringCompare, "_StringCompare"}, {&CodeGenerator::GenerateRegExpExec, "_RegExpExec"}, + {&CodeGenerator::GenerateNumberToString, "_NumberToString"}, }; diff --git a/src/heap.cc b/src/heap.cc index 54f3d08d1e..e90ae205b9 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -1767,6 +1767,7 @@ Object* Heap::SmiOrNumberFromDouble(double value, Object* Heap::NumberToString(Object* number) { + Counters::number_to_string_runtime.Increment(); Object* cached = GetNumberStringCache(number); if (cached != undefined_value()) { return cached; diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index 52899f2a92..f27a9060f6 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -5627,6 +5627,17 @@ void CodeGenerator::GenerateRegExpExec(ZoneList* args) { } +void CodeGenerator::GenerateNumberToString(ZoneList* args) { + ASSERT_EQ(args->length(), 1); + + // Load the argument on the stack and call the stub. + Load(args->at(0)); + NumberToStringStub stub; + Result result = frame_->CallStub(&stub, 1); + frame_->Push(&result); +} + + void CodeGenerator::VisitCallRuntime(CallRuntime* node) { if (CheckForInlineRuntimeCall(node)) { return; @@ -7782,38 +7793,10 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { __ 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. - // Load the number string cache. - ExternalReference roots_address = ExternalReference::roots_address(); - __ mov(ecx, Immediate(Heap::kNumberStringCacheRootIndex)); - __ mov(ebx, - Operand::StaticArray(ecx, times_pointer_size, roots_address)); - // Make the hash mask from the length of the number string cache. It - // contains two elements (number and string) for each cache entry. - __ mov(ecx, FieldOperand(ebx, FixedArray::kLengthOffset)); - __ shr(ecx, 1); // Divide length by two (length is not a smi). - __ sub(Operand(ecx), 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. - __ mov(edi, eax); - __ SmiUntag(edi); - __ and_(edi, Operand(ecx)); - // Check if the entry is the smi we are looking for. - __ cmp(eax, - FieldOperand(ebx, - edi, - times_twice_pointer_size, - FixedArray::kHeaderSize)); - __ IncrementCounter(equal, &Counters::string_plus_smi_hit, 1); - __ IncrementCounter(not_equal, &Counters::string_plus_smi_miss, 1); - __ j(not_equal, &string1); + NumberToStringStub::GenerateLookupNumberStringCache( + masm, eax, edi, ebx, ecx, true, &string1); - // Get the string from the cache and call the string add stub to make the - // result. - __ mov(edi, - FieldOperand(ebx, - edi, - times_twice_pointer_size, - FixedArray::kHeaderSize + kPointerSize)); + // Call the string add stub to make the result. __ EnterInternalFrame(); __ push(edx); // Original first argument. __ push(edi); // Number to string result for second argument. @@ -8951,6 +8934,74 @@ 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) { + ASSERT(kSmiTag == 0); + __ test(object, Immediate(kSmiTagMask)); + __ j(not_zero, 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. + ExternalReference roots_address = ExternalReference::roots_address(); + __ mov(scratch, Immediate(Heap::kNumberStringCacheRootIndex)); + __ mov(number_string_cache, + Operand::StaticArray(scratch, times_pointer_size, roots_address)); + // Make the hash mask from the length of the number string cache. It + // contains two elements (number and string) for each cache entry. + __ mov(mask, FieldOperand(number_string_cache, FixedArray::kLengthOffset)); + __ shr(mask, 1); // Divide length by two (length is not a smi). + __ sub(Operand(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. + __ mov(scratch, object); + __ SmiUntag(scratch); + __ and_(scratch, Operand(mask)); + // Check if the entry is the smi we are looking for. + __ cmp(object, + FieldOperand(number_string_cache, + scratch, + times_twice_pointer_size, + FixedArray::kHeaderSize)); + __ j(not_equal, not_found); + + // Get the result from the cache. + __ mov(result, + FieldOperand(number_string_cache, + scratch, + times_twice_pointer_size, + FixedArray::kHeaderSize + kPointerSize)); + __ IncrementCounter(&Counters::number_to_string_native, 1); +} + + +void NumberToStringStub::Generate(MacroAssembler* masm) { + Label runtime; + + __ mov(ebx, Operand(esp, kPointerSize)); + + // Generate code to lookup number in the number string cache. + GenerateLookupNumberStringCache(masm, ebx, eax, ebx, ecx, false, &runtime); + __ ret(1 * kPointerSize); + + __ bind(&runtime); + // Handle number to string in the runtime system if not found in the cache. + __ TailCallRuntime(ExternalReference(Runtime::kNumberToString), 1, 1); +} + + void CompareStub::Generate(MacroAssembler* masm) { Label call_builtin, done; diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h index 44faea467c..b6e2652568 100644 --- a/src/ia32/codegen-ia32.h +++ b/src/ia32/codegen-ia32.h @@ -580,6 +580,9 @@ class CodeGenerator: public AstVisitor { // Support for direct calls from JavaScript to native RegExp code. void GenerateRegExpExec(ZoneList* args); + // Fast support for number to string. + void GenerateNumberToString(ZoneList* args); + // Simple condition analysis. enum ConditionAnalysis { ALWAYS_TRUE, @@ -860,6 +863,39 @@ class StringCompareStub: public StringStubBase { }; +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_IA32_CODEGEN_IA32_H_ diff --git a/src/runtime.js b/src/runtime.js index 10ef98eb19..3c31ddb7ef 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -178,7 +178,7 @@ function STRING_ADD_LEFT(y) { y = %_ValueOf(y); } else { y = IS_NUMBER(y) - ? %NumberToString(y) + ? %_NumberToString(y) : %ToString(%ToPrimitive(y, NO_HINT)); } } @@ -194,7 +194,7 @@ function STRING_ADD_RIGHT(y) { x = %_ValueOf(x); } else { x = IS_NUMBER(x) - ? %NumberToString(x) + ? %_NumberToString(x) : %ToString(%ToPrimitive(x, NO_HINT)); } } @@ -529,7 +529,7 @@ function ToNumber(x) { // ECMA-262, section 9.8, page 35. function ToString(x) { if (IS_STRING(x)) return x; - if (IS_NUMBER(x)) return %NumberToString(x); + if (IS_NUMBER(x)) return %_NumberToString(x); if (IS_BOOLEAN(x)) return x ? 'true' : 'false'; if (IS_UNDEFINED(x)) return 'undefined'; return (IS_NULL(x)) ? 'null' : %ToString(%DefaultString(x)); diff --git a/src/v8-counters.h b/src/v8-counters.h index 60b52fcfaf..eaac2dbbbe 100644 --- a/src/v8-counters.h +++ b/src/v8-counters.h @@ -168,8 +168,8 @@ namespace internal { SC(string_compare_runtime, V8.StringCompareRuntime) \ SC(regexp_entry_runtime, V8.RegExpEntryRuntime) \ SC(regexp_entry_native, V8.RegExpEntryNative) \ - SC(string_plus_smi_hit, V8.StringPlusSmiHit) \ - SC(string_plus_smi_miss, V8.StringPlusSmiMiss) + SC(number_to_string_native, V8.NumberToStringNative) \ + SC(number_to_string_runtime, V8.NumberToStringRuntime) // This file contains all the v8 counters that are in use. class Counters : AllStatic { diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index 253c82e0c6..e48ca12634 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -3972,6 +3972,17 @@ void CodeGenerator::GenerateRegExpExec(ZoneList* args) { } +void CodeGenerator::GenerateNumberToString(ZoneList* args) { + ASSERT_EQ(args->length(), 1); + + // 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); +} + + void CodeGenerator::GenerateStringAdd(ZoneList* args) { ASSERT_EQ(2, args->length()); diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h index 4b01d83996..605ee37b9a 100644 --- a/src/x64/codegen-x64.h +++ b/src/x64/codegen-x64.h @@ -577,6 +577,9 @@ class CodeGenerator: public AstVisitor { // Support for direct calls from JavaScript to native RegExp code. void GenerateRegExpExec(ZoneList* args); + // Fast support for number to string. + void GenerateNumberToString(ZoneList* args); + // Simple condition analysis. enum ConditionAnalysis { ALWAYS_TRUE,