Version 3.22.17

Harmony: Implement Math.trunc and Math.sign. (issue 2938)

Performance and stability improvements on all platforms.

git-svn-id: http://v8.googlecode.com/svn/trunk@17292 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/ChangeLog b/ChangeLog
index f1b1062..304e913 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2013-10-21: Version 3.22.17
+
+        Harmony: Implement Math.trunc and Math.sign. (issue 2938)
+
+        Performance and stability improvements on all platforms.
+
+
 2013-10-21: Version 3.22.16
 
         Performance and stability improvements on all platforms.
diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc
index bdcdbf8..8d237cd 100644
--- a/src/arm/code-stubs-arm.cc
+++ b/src/arm/code-stubs-arm.cc
@@ -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;
 }
 
 
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index c48385c..234a211 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -2067,6 +2067,11 @@
                "native harmony-array.js") == 0) {
       if (!CompileExperimentalBuiltin(isolate(), i)) return false;
     }
+    if (FLAG_harmony_maths &&
+        strcmp(ExperimentalNatives::GetScriptName(i).start(),
+               "native harmony-math.js") == 0) {
+      if (!CompileExperimentalBuiltin(isolate(), i)) return false;
+    }
   }
 
   InstallExperimentalNativeFunctions();
diff --git a/src/code-stubs-hydrogen.cc b/src/code-stubs-hydrogen.cc
index a695161..091d4fb 100644
--- a/src/code-stubs-hydrogen.cc
+++ b/src/code-stubs-hydrogen.cc
@@ -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
diff --git a/src/code-stubs.cc b/src/code-stubs.cc
index 0c5f389..e68a5dd 100644
--- a/src/code-stubs.cc
+++ b/src/code-stubs.cc
@@ -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;
   }
 
@@ -1127,6 +1121,12 @@
 }
 
 
+void NumberToStringStub::InstallDescriptors(Isolate* isolate) {
+  NumberToStringStub stub;
+  InstallDescriptor(isolate, &stub);
+}
+
+
 void FastNewClosureStub::InstallDescriptors(Isolate* isolate) {
   FastNewClosureStub stub(STRICT_MODE, false);
   InstallDescriptor(isolate, &stub);
diff --git a/src/code-stubs.h b/src/code-stubs.h
index b5f74b2..bd519e3 100644
--- a/src/code-stubs.h
+++ b/src/code-stubs.h
@@ -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;
 
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index e7180a0..45f0c72 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -182,6 +182,7 @@
             "enable harmony numeric literals (0o77, 0b11)")
 DEFINE_bool(harmony_strings, false, "enable harmony string")
 DEFINE_bool(harmony_arrays, false, "enable harmony arrays")
+DEFINE_bool(harmony_maths, false, "enable harmony math functions")
 DEFINE_bool(harmony, false, "enable all harmony features (except typeof)")
 DEFINE_implication(harmony, harmony_scoping)
 DEFINE_implication(harmony, harmony_modules)
@@ -194,6 +195,7 @@
 DEFINE_implication(harmony, harmony_numeric_literals)
 DEFINE_implication(harmony, harmony_strings)
 DEFINE_implication(harmony, harmony_arrays)
+DEFINE_implication(harmony, harmony_maths)
 DEFINE_implication(harmony_modules, harmony_scoping)
 DEFINE_implication(harmony_observation, harmony_collections)
 
diff --git a/src/harmony-math.js b/src/harmony-math.js
new file mode 100644
index 0000000..a4d3f2e
--- /dev/null
+++ b/src/harmony-math.js
@@ -0,0 +1,60 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+'use strict';
+
+// ES6 draft 09-27-13, section 20.2.2.28.
+function MathSign(x) {
+  x = TO_NUMBER_INLINE(x);
+  if (x > 0) return 1;
+  if (x < 0) return -1;
+  if (x === 0) return x;
+  return NAN;
+}
+
+
+// ES6 draft 09-27-13, section 20.2.2.34.
+function MathTrunc(x) {
+  x = TO_NUMBER_INLINE(x);
+  if (x > 0) return MathFloor(x);
+  if (x < 0) return MathCeil(x);
+  if (x === 0) return x;
+  return NAN;
+}
+
+
+function ExtendMath() {
+  %CheckIsBootstrapping();
+
+  // Set up the non-enumerable functions on the Math object.
+  InstallFunctions($Math, DONT_ENUM, $Array(
+    "sign", MathSign,
+    "trunc", MathTrunc
+  ));
+}
+
+ExtendMath();
diff --git a/src/hydrogen.cc b/src/hydrogen.cc
index 14ac329..fbca58f 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -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);
-
-      // 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();
+    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();
       {
-        // 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();
+        // 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);
+
+        // 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();
@@ -7850,6 +7843,42 @@
     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);
     right = EnforceNumberType(right, right_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);
 }
 
diff --git a/src/hydrogen.h b/src/hydrogen.h
index bd28ba1..4159602 100644
--- a/src/hydrogen.h
+++ b/src/hydrogen.h
@@ -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,
diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc
index 71f7989..5a7e2f7 100644
--- a/src/ia32/code-stubs-ia32.cc
+++ b/src/ia32/code-stubs-ia32.cc
@@ -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;
 }
 
 
diff --git a/src/isolate.cc b/src/isolate.cc
index 29ead29..71cd301 100644
--- a/src/isolate.cc
+++ b/src/isolate.cc
@@ -2327,6 +2327,7 @@
     ArrayConstructorStubBase::InstallDescriptors(this);
     InternalArrayConstructorStubBase::InstallDescriptors(this);
     FastNewClosureStub::InstallDescriptors(this);
+    NumberToStringStub::InstallDescriptors(this);
   }
 
   if (FLAG_sweeper_threads > 0) {
diff --git a/src/math.js b/src/math.js
index 47938a3..efab63a 100644
--- a/src/math.js
+++ b/src/math.js
@@ -45,59 +45,51 @@
 // ECMA 262 - 15.8.2.1
 function MathAbs(x) {
   if (%_IsSmi(x)) return x >= 0 ? x : -x;
-  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+  x = TO_NUMBER_INLINE(x);
   if (x === 0) return 0;  // To handle -0.
   return x > 0 ? x : -x;
 }
 
 // ECMA 262 - 15.8.2.2
 function MathAcos(x) {
-  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
-  return %Math_acos(x);
+  return %Math_acos(TO_NUMBER_INLINE(x));
 }
 
 // ECMA 262 - 15.8.2.3
 function MathAsin(x) {
-  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
-  return %Math_asin(x);
+  return %Math_asin(TO_NUMBER_INLINE(x));
 }
 
 // ECMA 262 - 15.8.2.4
 function MathAtan(x) {
-  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
-  return %Math_atan(x);
+  return %Math_atan(TO_NUMBER_INLINE(x));
 }
 
 // ECMA 262 - 15.8.2.5
 // The naming of y and x matches the spec, as does the order in which
 // ToNumber (valueOf) is called.
 function MathAtan2(y, x) {
-  if (!IS_NUMBER(y)) y = NonNumberToNumber(y);
-  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
-  return %Math_atan2(y, x);
+  return %Math_atan2(TO_NUMBER_INLINE(y), TO_NUMBER_INLINE(x));
 }
 
 // ECMA 262 - 15.8.2.6
 function MathCeil(x) {
-  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
-  return %Math_ceil(x);
+  return %Math_ceil(TO_NUMBER_INLINE(x));
 }
 
 // ECMA 262 - 15.8.2.7
 function MathCos(x) {
-  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
-  return %_MathCos(x);
+  return %_MathCos(TO_NUMBER_INLINE(x));
 }
 
 // ECMA 262 - 15.8.2.8
 function MathExp(x) {
-  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
-  return %Math_exp(x);
+  return %Math_exp(TO_NUMBER_INLINE(x));
 }
 
 // ECMA 262 - 15.8.2.9
 function MathFloor(x) {
-  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
+  x = TO_NUMBER_INLINE(x);
   // It's more common to call this with a positive number that's out
   // of range than negative numbers; check the upper bound first.
   if (x < 0x80000000 && x > 0) {
@@ -113,16 +105,15 @@
 
 // ECMA 262 - 15.8.2.10
 function MathLog(x) {
-  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
-  return %_MathLog(x);
+  return %_MathLog(TO_NUMBER_INLINE(x));
 }
 
 // ECMA 262 - 15.8.2.11
 function MathMax(arg1, arg2) {  // length == 2
   var length = %_ArgumentsLength();
   if (length == 2) {
-    if (!IS_NUMBER(arg1)) arg1 = NonNumberToNumber(arg1);
-    if (!IS_NUMBER(arg2)) arg2 = NonNumberToNumber(arg2);
+    arg1 = TO_NUMBER_INLINE(arg1);
+    arg2 = TO_NUMBER_INLINE(arg2);
     if (arg2 > arg1) return arg2;
     if (arg1 > arg2) return arg1;
     if (arg1 == arg2) {
@@ -151,8 +142,8 @@
 function MathMin(arg1, arg2) {  // length == 2
   var length = %_ArgumentsLength();
   if (length == 2) {
-    if (!IS_NUMBER(arg1)) arg1 = NonNumberToNumber(arg1);
-    if (!IS_NUMBER(arg2)) arg2 = NonNumberToNumber(arg2);
+    arg1 = TO_NUMBER_INLINE(arg1);
+    arg2 = TO_NUMBER_INLINE(arg2);
     if (arg2 > arg1) return arg1;
     if (arg1 > arg2) return arg2;
     if (arg1 == arg2) {
@@ -179,9 +170,7 @@
 
 // ECMA 262 - 15.8.2.13
 function MathPow(x, y) {
-  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
-  if (!IS_NUMBER(y)) y = NonNumberToNumber(y);
-  return %_MathPow(x, y);
+  return %_MathPow(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y));
 }
 
 // ECMA 262 - 15.8.2.14
@@ -191,33 +180,27 @@
 
 // ECMA 262 - 15.8.2.15
 function MathRound(x) {
-  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
-  return %RoundNumber(x);
+  return %RoundNumber(TO_NUMBER_INLINE(x));
 }
 
 // ECMA 262 - 15.8.2.16
 function MathSin(x) {
-  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
-  return %_MathSin(x);
+  return %_MathSin(TO_NUMBER_INLINE(x));
 }
 
 // ECMA 262 - 15.8.2.17
 function MathSqrt(x) {
-  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
-  return %_MathSqrt(x);
+  return %_MathSqrt(TO_NUMBER_INLINE(x));
 }
 
 // ECMA 262 - 15.8.2.18
 function MathTan(x) {
-  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
-  return %_MathTan(x);
+  return %_MathTan(TO_NUMBER_INLINE(x));
 }
 
 // Non-standard extension.
 function MathImul(x, y) {
-  if (!IS_NUMBER(x)) x = NonNumberToNumber(x);
-  if (!IS_NUMBER(y)) y = NonNumberToNumber(y);
-  return %NumberImul(x, y);
+  return %NumberImul(TO_NUMBER_INLINE(x), TO_NUMBER_INLINE(y));
 }
 
 
diff --git a/src/version.cc b/src/version.cc
index 9085561..efcce67 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -34,7 +34,7 @@
 // system so their names cannot be changed without changing the scripts.
 #define MAJOR_VERSION     3
 #define MINOR_VERSION     22
-#define BUILD_NUMBER      16
+#define BUILD_NUMBER      17
 #define PATCH_LEVEL       0
 // Use 1 for candidates and 0 otherwise.
 // (Boolean macro values are not supported by all preprocessors.)
diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc
index a81c780..dfbbd5c 100644
--- a/src/x64/code-stubs-x64.cc
+++ b/src/x64/code-stubs-x64.cc
@@ -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;
 }
 
 
diff --git a/test/mjsunit/harmony/math-sign.js b/test/mjsunit/harmony/math-sign.js
new file mode 100644
index 0000000..8a89d62
--- /dev/null
+++ b/test/mjsunit/harmony/math-sign.js
@@ -0,0 +1,48 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --harmony-maths
+
+assertEquals("Infinity", String(1/Math.sign(0)));
+assertEquals("-Infinity", String(1/Math.sign(-0)));
+assertEquals(1, Math.sign(100));
+assertEquals(-1, Math.sign(-199));
+assertEquals(1, Math.sign(100.1));
+assertTrue(isNaN(Math.sign("abc")));
+assertTrue(isNaN(Math.sign({})));
+assertEquals(0, Math.sign([]));
+assertEquals(1, Math.sign([1]));
+assertEquals(-1, Math.sign([-100.1]));
+assertTrue(isNaN(Math.sign([1, 1])));
+assertEquals(1, Math.sign({ toString: function() { return "100"; } }));
+assertEquals(1, Math.sign({ toString: function() { return 100; } }));
+assertEquals(-1, Math.sign({ valueOf: function() { return -1.1; } }));
+assertEquals(-1, Math.sign({ valueOf: function() { return "-1.1"; } }));
+assertEquals(-1, Math.sign(-Infinity));
+assertEquals(1, Math.sign(Infinity));
+assertEquals(-1, Math.sign("-Infinity"));
+assertEquals(1, Math.sign("Infinity"));
diff --git a/test/mjsunit/harmony/math-trunc.js b/test/mjsunit/harmony/math-trunc.js
new file mode 100644
index 0000000..ed91ed1
--- /dev/null
+++ b/test/mjsunit/harmony/math-trunc.js
@@ -0,0 +1,51 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --harmony-maths
+
+assertEquals("Infinity", String(1/Math.trunc(0)));
+assertEquals("-Infinity", String(1/Math.trunc(-0)));
+assertEquals("Infinity", String(1/Math.trunc(Math.PI/4)));
+assertEquals("-Infinity", String(1/Math.trunc(-Math.sqrt(2)/2)));
+assertEquals(100, Math.trunc(100));
+assertEquals(-199, Math.trunc(-199));
+assertEquals(100, Math.trunc(100.1));
+assertTrue(isNaN(Math.trunc("abc")));
+assertTrue(isNaN(Math.trunc({})));
+assertEquals(0, Math.trunc([]));
+assertEquals(1, Math.trunc([1]));
+assertEquals(-100, Math.trunc([-100.1]));
+assertTrue(isNaN(Math.trunc([1, 1])));
+assertEquals(-100, Math.trunc({ toString: function() { return "-100.3"; } }));
+assertEquals(10, Math.trunc({ toString: function() { return 10.1; } }));
+assertEquals(-1, Math.trunc({ valueOf: function() { return -1.1; } }));
+assertEquals("-Infinity",
+             String(1/Math.trunc({ valueOf: function() { return "-0.1"; } })));
+assertEquals("-Infinity", String(Math.trunc(-Infinity)));
+assertEquals("Infinity", String(Math.trunc(Infinity)));
+assertEquals("-Infinity", String(Math.trunc("-Infinity")));
+assertEquals("Infinity", String(Math.trunc("Infinity")));
diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp
index 94b9bc0..22fcf94 100644
--- a/tools/gyp/v8.gyp
+++ b/tools/gyp/v8.gyp
@@ -936,6 +936,7 @@
           '../../src/array-iterator.js',
           '../../src/harmony-string.js',
           '../../src/harmony-array.js',
+          '../../src/harmony-math.js'
         ],
       },
       'actions': [