Replaced unary negation by multiplication with -1.
authorsvenpanne@chromium.org <svenpanne@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 2 Aug 2013 11:56:35 +0000 (11:56 +0000)
committersvenpanne@chromium.org <svenpanne@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 2 Aug 2013 11:56:35 +0000 (11:56 +0000)
This fixes a deopt loop in the Epic Citadel demo and removes some code. Apart from that, this change is performance-neutral.

When we do something similar for BIT_NOT, the whole UnaryOp stuff can go away.

R=jkummerow@chromium.org

Review URL: https://codereview.chromium.org/21782002

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

src/arm/full-codegen-arm.cc
src/code-stubs-hydrogen.cc
src/hydrogen.cc
src/ia32/full-codegen-ia32.cc
src/mips/full-codegen-mips.cc
src/parser.cc
src/typing.cc
src/x64/full-codegen-x64.cc
test/mjsunit/unary-minus-deopt.js [new file with mode: 0644]

index 3bcfc3b..dd34792 100644 (file)
@@ -4349,10 +4349,6 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
       break;
     }
 
-    case Token::SUB:
-      EmitUnaryOperation(expr, "[ UnaryOperation (SUB)");
-      break;
-
     case Token::BIT_NOT:
       EmitUnaryOperation(expr, "[ UnaryOperation (BIT_NOT)");
       break;
@@ -4365,6 +4361,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
 
 void FullCodeGenerator::EmitUnaryOperation(UnaryOperation* expr,
                                            const char* comment) {
+  ASSERT_EQ(Token::BIT_NOT, expr->op());
   // TODO(svenpanne): Allowing format strings in Comment would be nice here...
   Comment cmt(masm_, comment);
   UnaryOpStub stub(expr->op());
index 934f9a6..33681ed 100644 (file)
@@ -804,6 +804,7 @@ Handle<Code> CompareNilICStub::GenerateCode() {
 template <>
 HValue* CodeStubGraphBuilder<UnaryOpStub>::BuildCodeInitializedStub() {
   UnaryOpStub* stub = casted_stub();
+  ASSERT_EQ(Token::BIT_NOT, stub->operation());
   Handle<Type> type = stub->GetType(graph()->isolate());
   HValue* input = GetParameter(0);
 
index 496b6ad..7e20aaf 100644 (file)
@@ -1718,33 +1718,14 @@ HValue* HGraphBuilder::BuildCloneShallowArray(HValue* boilerplate,
 
 HInstruction* HGraphBuilder::BuildUnaryMathOp(
     HValue* input, Handle<Type> type, Token::Value operation) {
+  ASSERT_EQ(Token::BIT_NOT, operation);
   // We only handle the numeric cases here
   type = handle(
       Type::Intersect(type, handle(Type::Number(), isolate())), isolate());
-
-  switch (operation) {
-    default:
-      UNREACHABLE();
-    case Token::SUB: {
-        HInstruction* instr =
-            NewUncasted<HMul>(input, graph()->GetConstantMinus1());
-        Representation rep = Representation::FromType(type);
-        if (type->Is(Type::None())) {
-          Add<HDeoptimize>(Deoptimizer::SOFT);
-        }
-        if (instr->IsBinaryOperation()) {
-          HBinaryOperation* binop = HBinaryOperation::cast(instr);
-          binop->set_observed_input_representation(1, rep);
-          binop->set_observed_input_representation(2, rep);
-        }
-        return instr;
-      }
-    case Token::BIT_NOT:
-      if (type->Is(Type::None())) {
-        Add<HDeoptimize>(Deoptimizer::SOFT);
-      }
-      return New<HBitNot>(input);
+  if (type->Is(Type::None())) {
+    Add<HDeoptimize>(Deoptimizer::SOFT);
   }
+  return New<HBitNot>(input);
 }
 
 
@@ -7220,7 +7201,6 @@ void HOptimizedGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) {
     case Token::DELETE: return VisitDelete(expr);
     case Token::VOID: return VisitVoid(expr);
     case Token::TYPEOF: return VisitTypeof(expr);
-    case Token::SUB: return VisitSub(expr);
     case Token::BIT_NOT: return VisitBitNot(expr);
     case Token::NOT: return VisitNot(expr);
     default: UNREACHABLE();
@@ -7283,15 +7263,6 @@ void HOptimizedGraphBuilder::VisitTypeof(UnaryOperation* expr) {
 }
 
 
-void HOptimizedGraphBuilder::VisitSub(UnaryOperation* expr) {
-  CHECK_ALIVE(VisitForValue(expr->expression()));
-  Handle<Type> operand_type = expr->expression()->bounds().lower;
-  HValue* value = TruncateToNumber(Pop(), &operand_type);
-  HInstruction* instr = BuildUnaryMathOp(value, operand_type, Token::SUB);
-  return ast_context()->ReturnInstruction(instr, expr->id());
-}
-
-
 void HOptimizedGraphBuilder::VisitBitNot(UnaryOperation* expr) {
   CHECK_ALIVE(VisitForValue(expr->expression()));
   Handle<Type> operand_type = expr->expression()->bounds().lower;
index a5684f6..a3e17a6 100644 (file)
@@ -4347,10 +4347,6 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
       break;
     }
 
-    case Token::SUB:
-      EmitUnaryOperation(expr, "[ UnaryOperation (SUB)");
-      break;
-
     case Token::BIT_NOT:
       EmitUnaryOperation(expr, "[ UnaryOperation (BIT_NOT)");
       break;
@@ -4363,6 +4359,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
 
 void FullCodeGenerator::EmitUnaryOperation(UnaryOperation* expr,
                                            const char* comment) {
+  ASSERT_EQ(Token::BIT_NOT, expr->op());
   Comment cmt(masm_, comment);
   UnaryOpStub stub(expr->op());
   // UnaryOpStub expects the argument to be in the
index 3a627d7..0a26dbe 100644 (file)
@@ -4382,10 +4382,6 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
       break;
     }
 
-    case Token::SUB:
-      EmitUnaryOperation(expr, "[ UnaryOperation (SUB)");
-      break;
-
     case Token::BIT_NOT:
       EmitUnaryOperation(expr, "[ UnaryOperation (BIT_NOT)");
       break;
@@ -4398,6 +4394,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
 
 void FullCodeGenerator::EmitUnaryOperation(UnaryOperation* expr,
                                            const char* comment) {
+  ASSERT_EQ(Token::BIT_NOT, expr->op());
   // TODO(svenpanne): Allowing format strings in Comment would be nice here...
   Comment cmt(masm_, comment);
   UnaryOpStub stub(expr->op());
index df568ef..aa83969 100644 (file)
@@ -3197,6 +3197,13 @@ Expression* Parser::ParseUnaryExpression(bool* ok) {
                                            factory()->NewNumberLiteral(1),
                                            position);
     }
+    // The same idea for '-foo' => 'foo*(-1)'.
+    if (op == Token::SUB) {
+      return factory()->NewBinaryOperation(Token::MUL,
+                                           expression,
+                                           factory()->NewNumberLiteral(-1),
+                                           position);
+    }
 
     return factory()->NewUnaryOperation(op, expression, position);
 
index 4220d21..ad4fbdb 100644 (file)
@@ -419,13 +419,6 @@ void AstTyper::VisitUnaryOperation(UnaryOperation* expr) {
     case Token::VOID:
       NarrowType(expr, Bounds(Type::Undefined(), isolate_));
       break;
-    case Token::ADD:
-    case Token::SUB: {
-      Type* upper = *expr->expression()->bounds().upper;
-      if (!upper->Is(Type::Number())) upper = Type::Number();
-      NarrowType(expr, Bounds(Type::Smi(), upper, isolate_));
-      break;
-    }
     case Token::BIT_NOT:
       NarrowType(expr, Bounds(Type::Smi(), Type::Signed32(), isolate_));
       break;
index 1a4abe2..68301ec 100644 (file)
@@ -4335,10 +4335,6 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
       break;
     }
 
-    case Token::SUB:
-      EmitUnaryOperation(expr, "[ UnaryOperation (SUB)");
-      break;
-
     case Token::BIT_NOT:
       EmitUnaryOperation(expr, "[ UnaryOperation (BIT_NOT)");
       break;
@@ -4351,6 +4347,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
 
 void FullCodeGenerator::EmitUnaryOperation(UnaryOperation* expr,
                                            const char* comment) {
+  ASSERT_EQ(Token::BIT_NOT, expr->op());
   // TODO(svenpanne): Allowing format strings in Comment would be nice here...
   Comment cmt(masm_, comment);
   UnaryOpStub stub(expr->op());
diff --git a/test/mjsunit/unary-minus-deopt.js b/test/mjsunit/unary-minus-deopt.js
new file mode 100644 (file)
index 0000000..367ef75
--- /dev/null
@@ -0,0 +1,55 @@
+// 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: --allow-natives-syntax
+
+// This is a boiled-down example happening in the Epic Citadel demo:
+// After deopting, the multiplication for unary minus stayed in Smi
+// mode instead of going to double mode, leading to deopt loops.
+
+function unaryMinusTest(x) {
+  var g = (1 << x) | 0;
+  // Optimized code will contain a LMulI with -1 as right operand.
+  return (g & -g) - 1 | 0;
+}
+
+unaryMinusTest(3);
+unaryMinusTest(3);
+%OptimizeFunctionOnNextCall(unaryMinusTest);
+unaryMinusTest(3);
+assertOptimized(unaryMinusTest);
+
+// Deopt on kMinInt
+unaryMinusTest(31);
+// The following is normally true, but not with --stress-opt. :-/
+// assertUnoptimized(unaryMinusTest);
+
+// We should have learned something from the deopt.
+unaryMinusTest(31);
+%OptimizeFunctionOnNextCall(unaryMinusTest);
+unaryMinusTest(31);
+assertOptimized(unaryMinusTest);