Inline NumberToString
authorsgjesse@chromium.org <sgjesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 16 Feb 2010 09:41:11 +0000 (09:41 +0000)
committersgjesse@chromium.org <sgjesse@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 16 Feb 2010 09:41:11 +0000 (09:41 +0000)
NumberToString in runtime JavaScript is inlined through a call to a stub. Currently the stub only checks the number string cache and only if the number is a smi. Code is shared with the inlining of number string cache lookup when adding a smi to a string.
Review URL: http://codereview.chromium.org/604062

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3865 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/arm/codegen-arm.cc
src/arm/codegen-arm.h
src/code-stubs.h
src/codegen.cc
src/heap.cc
src/ia32/codegen-ia32.cc
src/ia32/codegen-ia32.h
src/runtime.js
src/v8-counters.h
src/x64/codegen-x64.cc
src/x64/codegen-x64.h

index 35dc7fe..4d9dc52 100644 (file)
@@ -3592,6 +3592,17 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* 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<Expression*>* args) {
   VirtualFrame::SpilledScope spilled_scope;
   ASSERT(args->length() == 2);
index 2578a39..10e28f4 100644 (file)
@@ -398,6 +398,9 @@ class CodeGenerator: public AstVisitor {
   // Support for direct calls from JavaScript to native RegExp code.
   void GenerateRegExpExec(ZoneList<Expression*>* args);
 
+  // Fast support for number to string.
+  void GenerateNumberToString(ZoneList<Expression*>* args);
+
   // Simple condition analysis.
   enum ConditionAnalysis {
     ALWAYS_TRUE,
index d502f14..3901a64 100644 (file)
@@ -55,6 +55,7 @@ namespace internal {
   V(CounterOp)                           \
   V(ArgumentsAccess)                     \
   V(RegExpExec)                          \
+  V(NumberToString)                      \
   V(CEntry)                              \
   V(JSEntry)                             \
   V(DebuggerStatement)
index 438ec1e..1c3ecc4 100644 (file)
@@ -372,6 +372,7 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = {
   {&CodeGenerator::GenerateSubString, "_SubString"},
   {&CodeGenerator::GenerateStringCompare, "_StringCompare"},
   {&CodeGenerator::GenerateRegExpExec, "_RegExpExec"},
+  {&CodeGenerator::GenerateNumberToString, "_NumberToString"},
 };
 
 
index 54f3d08..e90ae20 100644 (file)
@@ -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;
index 52899f2..f27a906 100644 (file)
@@ -5627,6 +5627,17 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* 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);
-
-      // 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));
+      NumberToStringStub::GenerateLookupNumberStringCache(
+          masm, eax, edi, ebx, ecx, true, &string1);
+
+      // 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;
 
index 44faea4..b6e2652 100644 (file)
@@ -580,6 +580,9 @@ class CodeGenerator: public AstVisitor {
   // Support for direct calls from JavaScript to native RegExp code.
   void GenerateRegExpExec(ZoneList<Expression*>* args);
 
+  // Fast support for number to string.
+  void GenerateNumberToString(ZoneList<Expression*>* 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_
index 10ef98e..3c31ddb 100644 (file)
@@ -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));
index 60b52fc..eaac2db 100644 (file)
@@ -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 {
index 253c82e..e48ca12 100644 (file)
@@ -3972,6 +3972,17 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* 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<Expression*>* args) {
   ASSERT_EQ(2, args->length());
 
index 4b01d83..605ee37 100644 (file)
@@ -577,6 +577,9 @@ class CodeGenerator: public AstVisitor {
   // Support for direct calls from JavaScript to native RegExp code.
   void GenerateRegExpExec(ZoneList<Expression*>* args);
 
+  // Fast support for number to string.
+  void GenerateNumberToString(ZoneList<Expression*>* args);
+
   // Simple condition analysis.
   enum ConditionAnalysis {
     ALWAYS_TRUE,