Revision: 17290
Author:   [email protected]
Date:     Mon Oct 21 12:42:08 2013 UTC
Log: Inline number to string conversion for string addition into BinaryOp(Stub).

This fixes a performance regression that was caused by converting the
BinaryOpStub to a Hydrogen code stub. It also fixes a leftover TODO wrt.
the handling of Number*String or String*Number versions of the stub.

[email protected]

Review URL: https://codereview.chromium.org/27674002
http://code.google.com/p/v8/source/detail?r=17290

Modified:
 /branches/bleeding_edge/src/arm/code-stubs-arm.cc
 /branches/bleeding_edge/src/code-stubs-hydrogen.cc
 /branches/bleeding_edge/src/code-stubs.cc
 /branches/bleeding_edge/src/code-stubs.h
 /branches/bleeding_edge/src/hydrogen.cc
 /branches/bleeding_edge/src/hydrogen.h
 /branches/bleeding_edge/src/ia32/code-stubs-ia32.cc
 /branches/bleeding_edge/src/isolate.cc
 /branches/bleeding_edge/src/x64/code-stubs-x64.cc

=======================================
--- /branches/bleeding_edge/src/arm/code-stubs-arm.cc Fri Oct 18 14:55:21 2013 UTC +++ /branches/bleeding_edge/src/arm/code-stubs-arm.cc Mon Oct 21 12:42:08 2013 UTC
@@ -65,7 +65,8 @@
   static Register registers[] = { r0 };
   descriptor->register_param_count_ = 1;
   descriptor->register_params_ = registers;
-  descriptor->deoptimization_handler_ = NULL;
+  descriptor->deoptimization_handler_ =
+      Runtime::FunctionForId(Runtime::kNumberToString)->entry;
 }


=======================================
--- /branches/bleeding_edge/src/code-stubs-hydrogen.cc Fri Oct 18 14:55:21 2013 UTC +++ /branches/bleeding_edge/src/code-stubs-hydrogen.cc Mon Oct 21 12:42:08 2013 UTC
@@ -351,7 +351,7 @@
 HValue* CodeStubGraphBuilder<NumberToStringStub>::BuildCodeStub() {
   info()->MarkAsSavesCallerDoubles();
   HValue* number = GetParameter(NumberToStringStub::kNumber);
-  return BuildNumberToString(number);
+  return BuildNumberToString(number, handle(Type::Number(), isolate()));
 }


@@ -881,35 +881,52 @@
   if (stub->operation() == Token::ADD &&
(left_type->Maybe(Type::String()) || right_type->Maybe(Type::String())) &&
       !left_type->Is(Type::String()) && !right_type->Is(Type::String())) {
-    // For the generic add stub a fast case for String add is performance
+ // For the generic add stub a fast case for string addition is performance
     // critical.
     if (left_type->Maybe(Type::String())) {
-      IfBuilder left_string(this);
-      left_string.If<HIsStringAndBranch>(left);
-      left_string.Then();
-      Push(Add<HStringAdd>(left, right, STRING_ADD_CHECK_RIGHT));
-      left_string.Else();
-      Push(AddInstruction(BuildBinaryOperation(stub->operation(),
-          left, right, left_type, right_type, result_type,
-          stub->fixed_right_arg(), true)));
-      left_string.End();
+      IfBuilder if_leftisstring(this);
+      if_leftisstring.If<HIsStringAndBranch>(left);
+      if_leftisstring.Then();
+      {
+        Push(AddInstruction(BuildBinaryOperation(
+                    stub->operation(), left, right,
+                    handle(Type::String(), isolate()), right_type,
+                    result_type, stub->fixed_right_arg(), true)));
+      }
+      if_leftisstring.Else();
+      {
+        Push(AddInstruction(BuildBinaryOperation(
+                    stub->operation(), left, right,
+                    left_type, right_type, result_type,
+                    stub->fixed_right_arg(), true)));
+      }
+      if_leftisstring.End();
       result = Pop();
     } else {
-      IfBuilder right_string(this);
-      right_string.If<HIsStringAndBranch>(right);
-      right_string.Then();
-      Push(Add<HStringAdd>(left, right, STRING_ADD_CHECK_LEFT));
-      right_string.Else();
-      Push(AddInstruction(BuildBinaryOperation(stub->operation(),
-          left, right, left_type, right_type, result_type,
-          stub->fixed_right_arg(), true)));
-      right_string.End();
+      IfBuilder if_rightisstring(this);
+      if_rightisstring.If<HIsStringAndBranch>(right);
+      if_rightisstring.Then();
+      {
+        Push(AddInstruction(BuildBinaryOperation(
+                    stub->operation(), left, right,
+                    left_type, handle(Type::String(), isolate()),
+                    result_type, stub->fixed_right_arg(), true)));
+      }
+      if_rightisstring.Else();
+      {
+        Push(AddInstruction(BuildBinaryOperation(
+                    stub->operation(), left, right,
+                    left_type, right_type, result_type,
+                    stub->fixed_right_arg(), true)));
+      }
+      if_rightisstring.End();
       result = Pop();
     }
   } else {
-    result = AddInstruction(BuildBinaryOperation(stub->operation(),
-        left, right, left_type, right_type, result_type,
-        stub->fixed_right_arg(), true));
+    result = AddInstruction(BuildBinaryOperation(
+            stub->operation(), left, right,
+            left_type, right_type, result_type,
+            stub->fixed_right_arg(), true));
   }

   // If we encounter a generic argument, the number conversion is
=======================================
--- /branches/bleeding_edge/src/code-stubs.cc   Fri Oct 18 14:55:21 2013 UTC
+++ /branches/bleeding_edge/src/code-stubs.cc   Mon Oct 21 12:42:08 2013 UTC
@@ -570,14 +570,8 @@

   State max_input = Max(left_state_, right_state_);

- // TODO(olivf) Instead of doing this normalization we should have a Hydrogen - // version of the LookupNumberStringCache to avoid a converting StringAddStub.
-  if (left_state_ == STRING && right_state_ < STRING) {
-    right_state_ = GENERIC;
-  } else if (right_state_ == STRING && left_state_ < STRING) {
-    left_state_ = GENERIC;
-  } else if (!has_int_result() && op_ != Token::SHR &&
-             max_input <= NUMBER && max_input > result_state_) {
+  if (!has_int_result() && op_ != Token::SHR &&
+      max_input <= NUMBER && max_input > result_state_) {
     result_state_ = max_input;
   }

@@ -1125,6 +1119,12 @@
   ArrayNArgumentsConstructorStub stub3(GetInitialFastElementsKind());
   InstallDescriptor(isolate, &stub3);
 }
+
+
+void NumberToStringStub::InstallDescriptors(Isolate* isolate) {
+  NumberToStringStub stub;
+  InstallDescriptor(isolate, &stub);
+}


 void FastNewClosureStub::InstallDescriptors(Isolate* isolate) {
=======================================
--- /branches/bleeding_edge/src/code-stubs.h    Fri Oct 18 14:55:21 2013 UTC
+++ /branches/bleeding_edge/src/code-stubs.h    Mon Oct 21 12:42:08 2013 UTC
@@ -477,6 +477,8 @@
       Isolate* isolate,
       CodeStubInterfaceDescriptor* descriptor) V8_OVERRIDE;

+  static void InstallDescriptors(Isolate* isolate);
+
   // Parameters accessed via CodeStubGraphBuilder::GetParameter()
   static const int kNumber = 0;

=======================================
--- /branches/bleeding_edge/src/hydrogen.cc     Mon Oct 21 12:32:38 2013 UTC
+++ /branches/bleeding_edge/src/hydrogen.cc     Mon Oct 21 12:42:08 2013 UTC
@@ -1283,9 +1283,10 @@
 }


-HValue* HGraphBuilder::BuildLookupNumberStringCache(
-    HValue* object,
-    HIfContinuation* continuation) {
+HValue* HGraphBuilder::BuildNumberToString(HValue* object,
+                                           Handle<Type> type) {
+  NoObservableSideEffectsScope scope(this);
+
   // Create a joinable continuation.
   HIfContinuation found(graph()->CreateBasicBlock(),
                         graph()->CreateBasicBlock());
@@ -1294,7 +1295,7 @@
   HValue* number_string_cache =
       Add<HLoadRoot>(Heap::kNumberStringCacheRootIndex);

-  // Make the hash maks from the length of the number string cache. It
+  // Make the hash mask from the length of the number string cache. It
   // contains two elements (number and string) for each cache entry.
   HValue* mask = AddLoadFixedArrayLength(number_string_cache);
   mask->set_type(HType::Smi());
@@ -1317,7 +1318,7 @@

     // Check if object == key.
     IfBuilder if_objectiskey(this);
-    if_objectiskey.If<HCompareObjectEqAndBranch>(key, object);
+    if_objectiskey.If<HCompareObjectEqAndBranch>(object, key);
     if_objectiskey.Then();
     {
       // Make the key_index available.
@@ -1327,92 +1328,84 @@
   }
   if_objectissmi.Else();
   {
-    // Check if object is a heap number.
-    IfBuilder if_objectisnumber(this);
-    if_objectisnumber.If<HCompareMap>(
-        object, isolate()->factory()->heap_number_map());
-    if_objectisnumber.Then();
-    {
-      // Compute hash for heap number similar to double_get_hash().
-      HValue* low = Add<HLoadNamedField>(
-          object, HObjectAccess::ForHeapNumberValueLowestBits());
-      HValue* high = Add<HLoadNamedField>(
-          object, HObjectAccess::ForHeapNumberValueHighestBits());
-      HValue* hash = Add<HBitwise>(Token::BIT_XOR, low, high);
-      hash = Add<HBitwise>(Token::BIT_AND, hash, mask);
+    if (type->Is(Type::Smi())) {
+      if_objectissmi.Deopt("Excepted smi");
+    } else {
+      // Check if the object is a heap number.
+      IfBuilder if_objectisnumber(this);
+      if_objectisnumber.If<HCompareMap>(
+          object, isolate()->factory()->heap_number_map());
+      if_objectisnumber.Then();
+      {
+        // Compute hash for heap number similar to double_get_hash().
+        HValue* low = Add<HLoadNamedField>(
+            object, HObjectAccess::ForHeapNumberValueLowestBits());
+        HValue* high = Add<HLoadNamedField>(
+            object, HObjectAccess::ForHeapNumberValueHighestBits());
+        HValue* hash = Add<HBitwise>(Token::BIT_XOR, low, high);
+        hash = Add<HBitwise>(Token::BIT_AND, hash, mask);

-      // Load the key.
-      HValue* key_index = Add<HShl>(hash, graph()->GetConstant1());
-      HValue* key = Add<HLoadKeyed>(number_string_cache, key_index,
-                                    static_cast<HValue*>(NULL),
-                                    FAST_ELEMENTS, ALLOW_RETURN_HOLE);
+        // Load the key.
+        HValue* key_index = Add<HShl>(hash, graph()->GetConstant1());
+        HValue* key = Add<HLoadKeyed>(number_string_cache, key_index,
+                                      static_cast<HValue*>(NULL),
+                                      FAST_ELEMENTS, ALLOW_RETURN_HOLE);

-      // Check if key is a heap number.
-      IfBuilder if_keyisnumber(this);
-      if_keyisnumber.IfNot<HIsSmiAndBranch>(key);
-      if_keyisnumber.AndIf<HCompareMap>(
-          key, isolate()->factory()->heap_number_map());
-      if_keyisnumber.Then();
-      {
-        // Check if values of key and object match.
-        IfBuilder if_keyeqobject(this);
-        if_keyeqobject.If<HCompareNumericAndBranch>(
-            Add<HLoadNamedField>(key, HObjectAccess::ForHeapNumberValue()),
- Add<HLoadNamedField>(object, HObjectAccess::ForHeapNumberValue()),
-            Token::EQ);
-        if_keyeqobject.Then();
+ // Check if key is a heap number (the number string cache contains only + // SMIs and heap number, so it is sufficient to do a SMI check here).
+        IfBuilder if_keyisnotsmi(this);
+        if_keyisnotsmi.IfNot<HIsSmiAndBranch>(key);
+        if_keyisnotsmi.Then();
         {
-          // Make the key_index available.
-          Push(key_index);
+          // Check if values of key and object match.
+          IfBuilder if_keyeqobject(this);
+          if_keyeqobject.If<HCompareNumericAndBranch>(
+ Add<HLoadNamedField>(key, HObjectAccess::ForHeapNumberValue()), + Add<HLoadNamedField>(object, HObjectAccess::ForHeapNumberValue()),
+              Token::EQ);
+          if_keyeqobject.Then();
+          {
+            // Make the key_index available.
+            Push(key_index);
+          }
+          if_keyeqobject.JoinContinuation(&found);
         }
-        if_keyeqobject.JoinContinuation(&found);
+        if_keyisnotsmi.JoinContinuation(&found);
       }
-      if_keyisnumber.JoinContinuation(&found);
+      if_objectisnumber.Else();
+      {
+        if (type->Is(Type::Number())) {
+          if_objectisnumber.Deopt("Expected heap number");
+        }
+      }
+      if_objectisnumber.JoinContinuation(&found);
     }
-    if_objectisnumber.JoinContinuation(&found);
   }
-  if_objectissmi.End();
+  if_objectissmi.JoinContinuation(&found);

   // Check for cache hit.
   IfBuilder if_found(this, &found);
   if_found.Then();
+  {
+    // Count number to string operation in native code.
+    AddIncrementCounter(isolate()->counters()->number_to_string_native());

-  // Load the value in case of cache hit.
-  HValue* key_index = Pop();
-  HValue* value_index = Add<HAdd>(key_index, graph()->GetConstant1());
-  HValue* value = Add<HLoadKeyed>(number_string_cache, value_index,
-                                  static_cast<HValue*>(NULL),
-                                  FAST_ELEMENTS, ALLOW_RETURN_HOLE);
-  AddIncrementCounter(isolate()->counters()->number_to_string_native());
-
-  if_found.CaptureContinuation(continuation);
-
-  // The value is only available in true branch of continuation.
-  return value;
-}
-
-
-HValue* HGraphBuilder::BuildNumberToString(HValue* number) {
-  NoObservableSideEffectsScope scope(this);
-
-  // Lookup the number in the number string cache.
-  HIfContinuation continuation;
-  HValue* value = BuildLookupNumberStringCache(number, &continuation);
-  IfBuilder if_found(this, &continuation);
-  if_found.Then();
-
-  // Cache hit.
-  Push(value);
-
+    // Load the value in case of cache hit.
+    HValue* key_index = Pop();
+    HValue* value_index = Add<HAdd>(key_index, graph()->GetConstant1());
+    Push(Add<HLoadKeyed>(number_string_cache, value_index,
+                         static_cast<HValue*>(NULL),
+                         FAST_ELEMENTS, ALLOW_RETURN_HOLE));
+  }
   if_found.Else();
-
-  // Cache miss, fallback to runtime.
-  Add<HPushArgument>(number);
-  Push(Add<HCallRuntime>(
-          isolate()->factory()->empty_string(),
-          Runtime::FunctionForId(Runtime::kNumberToStringSkipCache),
-          1));
-
+  {
+    // Cache miss, fallback to runtime.
+    Add<HPushArgument>(object);
+    Push(Add<HCallRuntime>(
+            isolate()->factory()->empty_string(),
+            Runtime::FunctionForId(Runtime::kNumberToStringSkipCache),
+            1));
+  }
   if_found.End();

   return Pop();
@@ -7849,6 +7842,42 @@
     if (!maybe_string_add) right = TruncateToNumber(right, &right_type);
     right_rep = Representation::FromType(right_type);
   }
+
+  // Special case for string addition here.
+  if (op == Token::ADD &&
+      (left_type->Is(Type::String()) || right_type->Is(Type::String()))) {
+    if (left_type->Is(Type::String())) {
+      IfBuilder if_isstring(this);
+      if_isstring.If<HIsStringAndBranch>(left);
+      if_isstring.Then();
+      if_isstring.ElseDeopt("Expected string for LHS of binary operation");
+    } else if (left_type->Is(Type::Number())) {
+      left = BuildNumberToString(left, left_type);
+    } else {
+      ASSERT(right_type->Is(Type::String()));
+      HValue* function = AddLoadJSBuiltin(Builtins::STRING_ADD_RIGHT);
+      Add<HPushArgument>(left);
+      Add<HPushArgument>(right);
+      return NewUncasted<HInvokeFunction>(function, 2);
+    }
+
+    if (right_type->Is(Type::String())) {
+      IfBuilder if_isstring(this);
+      if_isstring.If<HIsStringAndBranch>(right);
+      if_isstring.Then();
+      if_isstring.ElseDeopt("Expected string for RHS of binary operation");
+    } else if (right_type->Is(Type::Number())) {
+      right = BuildNumberToString(right, right_type);
+    } else {
+      ASSERT(left_type->Is(Type::String()));
+      HValue* function = AddLoadJSBuiltin(Builtins::STRING_ADD_LEFT);
+      Add<HPushArgument>(left);
+      Add<HPushArgument>(right);
+      return NewUncasted<HInvokeFunction>(function, 2);
+    }
+
+    return NewUncasted<HStringAdd>(left, right, STRING_ADD_CHECK_NONE);
+  }

   if (binop_stub) {
     left = EnforceNumberType(left, left_type);
@@ -7859,15 +7888,12 @@

   bool is_non_primitive = (left_rep.IsTagged() && !left_rep.IsSmi()) ||
                           (right_rep.IsTagged() && !right_rep.IsSmi());
-  bool is_string_add    = op == Token::ADD &&
-                          (left_type->Is(Type::String()) ||
-                           right_type->Is(Type::String()));

   HInstruction* instr = NULL;
// Only the stub is allowed to call into the runtime, since otherwise we would // inline several instructions (including the two pushes) for every tagged // operation in optimized code, which is more expensive, than a stub call.
-  if (binop_stub && is_non_primitive && !is_string_add) {
+  if (binop_stub && is_non_primitive) {
     HValue* function = AddLoadJSBuiltin(BinaryOpIC::TokenToJSBuiltin(op));
     Add<HPushArgument>(left);
     Add<HPushArgument>(right);
@@ -7875,23 +7901,7 @@
   } else {
     switch (op) {
       case Token::ADD:
-        if (is_string_add) {
-          StringAddFlags flags = STRING_ADD_CHECK_BOTH;
-          if (left_type->Is(Type::String())) {
-            BuildCheckHeapObject(left);
-            AddInstruction(HCheckInstanceType::NewIsString(left, zone()));
-            flags = STRING_ADD_CHECK_RIGHT;
-          }
-          if (right_type->Is(Type::String())) {
-            BuildCheckHeapObject(right);
-            AddInstruction(HCheckInstanceType::NewIsString(right, zone()));
-            flags = (flags == STRING_ADD_CHECK_BOTH)
-                ? STRING_ADD_CHECK_LEFT : STRING_ADD_CHECK_NONE;
-          }
-          instr = NewUncasted<HStringAdd>(left, right, flags);
-        } else {
-          instr = NewUncasted<HAdd>(left, right);
-        }
+        instr = NewUncasted<HAdd>(left, right);
         break;
       case Token::SUB:
         instr = NewUncasted<HSub>(left, right);
@@ -9090,7 +9100,8 @@
   ASSERT_EQ(1, call->arguments()->length());
   CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
   HValue* number = Pop();
-  HValue* result = BuildNumberToString(number);
+  HValue* result = BuildNumberToString(
+      number, handle(Type::Number(), isolate()));
   return ast_context()->ReturnValue(result);
 }

=======================================
--- /branches/bleeding_edge/src/hydrogen.h      Wed Oct 16 08:10:36 2013 UTC
+++ /branches/bleeding_edge/src/hydrogen.h      Mon Oct 21 12:42:08 2013 UTC
@@ -1234,14 +1234,7 @@
                                    ElementsKind to_kind,
                                    bool is_jsarray);

-  // Do lookup in the number string cache. If the object is not found
-  // in the cache, the false branch of the continuation is taken;
-  // otherwise the true branch is taken and the returned value contains
-  // the cache value for the object. The returned value must NOT be used
-  // on the false branch.
-  HValue* BuildLookupNumberStringCache(HValue* object,
-                                       HIfContinuation* continuation);
-  HValue* BuildNumberToString(HValue* number);
+  HValue* BuildNumberToString(HValue* object, Handle<Type> type);

   HInstruction* BuildUncheckedMonomorphicElementAccess(
       HValue* checked_object,
=======================================
--- /branches/bleeding_edge/src/ia32/code-stubs-ia32.cc Fri Oct 18 14:55:21 2013 UTC +++ /branches/bleeding_edge/src/ia32/code-stubs-ia32.cc Mon Oct 21 12:42:08 2013 UTC
@@ -70,7 +70,8 @@
   static Register registers[] = { eax };
   descriptor->register_param_count_ = 1;
   descriptor->register_params_ = registers;
-  descriptor->deoptimization_handler_ = NULL;
+  descriptor->deoptimization_handler_ =
+      Runtime::FunctionForId(Runtime::kNumberToString)->entry;
 }


=======================================
--- /branches/bleeding_edge/src/isolate.cc      Fri Oct  4 08:17:11 2013 UTC
+++ /branches/bleeding_edge/src/isolate.cc      Mon Oct 21 12:42:08 2013 UTC
@@ -2327,6 +2327,7 @@
     ArrayConstructorStubBase::InstallDescriptors(this);
     InternalArrayConstructorStubBase::InstallDescriptors(this);
     FastNewClosureStub::InstallDescriptors(this);
+    NumberToStringStub::InstallDescriptors(this);
   }

   if (FLAG_sweeper_threads > 0) {
=======================================
--- /branches/bleeding_edge/src/x64/code-stubs-x64.cc Fri Oct 18 14:55:21 2013 UTC +++ /branches/bleeding_edge/src/x64/code-stubs-x64.cc Mon Oct 21 12:42:08 2013 UTC
@@ -66,7 +66,8 @@
   static Register registers[] = { rax };
   descriptor->register_param_count_ = 1;
   descriptor->register_params_ = registers;
-  descriptor->deoptimization_handler_ = NULL;
+  descriptor->deoptimization_handler_ =
+      Runtime::FunctionForId(Runtime::kNumberToString)->entry;
 }


--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to