Extend JSBuiltinReducer to cover Math.abs as well.
authormstarzinger@chromium.org <mstarzinger@chromium.org>
Fri, 26 Sep 2014 14:06:56 +0000 (14:06 +0000)
committermstarzinger@chromium.org <mstarzinger@chromium.org>
Fri, 26 Sep 2014 14:06:56 +0000 (14:06 +0000)
R=titzer@chromium.org
TEST=compiler-unittests/JSBuiltinReducerTest.MathAbs

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

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

src/compiler/graph-unittest.cc
src/compiler/graph-unittest.h
src/compiler/js-builtin-reducer-unittest.cc
src/compiler/js-builtin-reducer.cc
src/compiler/js-builtin-reducer.h
test/mjsunit/asm/math-abs.js [new file with mode: 0644]

index 0071b81..35585e8 100644 (file)
@@ -744,6 +744,7 @@ Matcher<Node*> IsStore(const Matcher<MachineType>& type_matcher,
         new IsBinopMatcher(IrOpcode::k##Name, lhs_matcher, rhs_matcher)); \
   }
 IS_BINOP_MATCHER(NumberLessThan)
+IS_BINOP_MATCHER(NumberSubtract)
 IS_BINOP_MATCHER(Word32And)
 IS_BINOP_MATCHER(Word32Sar)
 IS_BINOP_MATCHER(Word32Shl)
index 04d4af0..b821165 100644 (file)
@@ -87,6 +87,8 @@ Matcher<Node*> IsCall(const Matcher<CallDescriptor*>& descriptor_matcher,
 
 Matcher<Node*> IsNumberLessThan(const Matcher<Node*>& lhs_matcher,
                                 const Matcher<Node*>& rhs_matcher);
+Matcher<Node*> IsNumberSubtract(const Matcher<Node*>& lhs_matcher,
+                                const Matcher<Node*>& rhs_matcher);
 
 Matcher<Node*> IsLoad(const Matcher<LoadRepresentation>& rep_matcher,
                       const Matcher<Node*>& base_matcher,
index 598a553..5177d8d 100644 (file)
@@ -60,6 +60,39 @@ Type* const kNumberTypes[] = {
 
 
 // -----------------------------------------------------------------------------
+// Math.abs
+
+
+TEST_F(JSBuiltinReducerTest, MathAbs) {
+  Handle<JSFunction> f(isolate()->context()->math_abs_fun());
+
+  TRACED_FOREACH(Type*, t0, kNumberTypes) {
+    Node* p0 = Parameter(t0, 0);
+    Node* fun = HeapConstant(Unique<HeapObject>::CreateUninitialized(f));
+    Node* call = graph()->NewNode(javascript()->Call(3, NO_CALL_FUNCTION_FLAGS),
+                                  fun, UndefinedConstant(), p0);
+    Reduction r = Reduce(call);
+
+    if (t0->Is(Type::Unsigned32())) {
+      ASSERT_TRUE(r.Changed());
+      EXPECT_THAT(r.replacement(), p0);
+    } else {
+      Capture<Node*> branch;
+      ASSERT_TRUE(r.Changed());
+      EXPECT_THAT(
+          r.replacement(),
+          IsPhi(kMachNone, p0, IsNumberSubtract(IsNumberConstant(0), p0),
+                IsMerge(IsIfTrue(CaptureEq(&branch)),
+                        IsIfFalse(AllOf(
+                            CaptureEq(&branch),
+                            IsBranch(IsNumberLessThan(IsNumberConstant(0), p0),
+                                     graph()->start()))))));
+    }
+  }
+}
+
+
+// -----------------------------------------------------------------------------
 // Math.sqrt
 
 
index f1aef31..ec73742 100644 (file)
@@ -95,6 +95,33 @@ class JSCallReduction {
 };
 
 
+// ECMA-262, section 15.8.2.1.
+Reduction JSBuiltinReducer::ReduceMathAbs(Node* node) {
+  JSCallReduction r(node);
+  if (r.InputsMatchOne(Type::Unsigned32())) {
+    // Math.abs(a:uint32) -> a
+    return Replace(r.left());
+  }
+  if (r.InputsMatchOne(Type::Number())) {
+    // Math.abs(a:number) -> (a > 0 ? a : 0 - a)
+    Node* value = r.left();
+    Node* zero = jsgraph()->ZeroConstant();
+    Node* control = graph()->start();
+    Node* tag = graph()->NewNode(simplified()->NumberLessThan(), zero, value);
+
+    Node* branch = graph()->NewNode(common()->Branch(), tag, control);
+    Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
+    Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
+    Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
+
+    Node* neg = graph()->NewNode(simplified()->NumberSubtract(), zero, value);
+    value = graph()->NewNode(common()->Phi(kMachNone, 2), value, neg, merge);
+    return Replace(value);
+  }
+  return NoChange();
+}
+
+
 // ECMA-262, section 15.8.2.17.
 Reduction JSBuiltinReducer::ReduceMathSqrt(Node* node) {
   JSCallReduction r(node);
@@ -170,6 +197,8 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
   // Dispatch according to the BuiltinFunctionId if present.
   if (!r.HasBuiltinFunctionId()) return NoChange();
   switch (r.GetBuiltinFunctionId()) {
+    case kMathAbs:
+      return ReplaceWithPureReduction(node, ReduceMathAbs(node));
     case kMathSqrt:
       return ReplaceWithPureReduction(node, ReduceMathSqrt(node));
     case kMathMax:
index e5a1b83..f3b862f 100644 (file)
@@ -30,6 +30,7 @@ class JSBuiltinReducer FINAL : public Reducer {
   MachineOperatorBuilder* machine() const { return jsgraph_->machine(); }
   SimplifiedOperatorBuilder* simplified() { return &simplified_; }
 
+  Reduction ReduceMathAbs(Node* node);
   Reduction ReduceMathSqrt(Node* node);
   Reduction ReduceMathMax(Node* node);
   Reduction ReduceMathImul(Node* node);
diff --git a/test/mjsunit/asm/math-abs.js b/test/mjsunit/asm/math-abs.js
new file mode 100644 (file)
index 0000000..6387749
--- /dev/null
@@ -0,0 +1,84 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+function Module(stdlib) {
+  "use asm";
+
+  var abs = stdlib.Math.abs;
+
+  // f: double -> double
+  function f(a) {
+    a = +a;
+    return +abs(a);
+  }
+
+  // g: unsigned -> double
+  function g(a) {
+    a = a>>>0;
+    return +abs(a);
+  }
+
+  // h: signed -> double
+  function h(a) {
+    a = a|0;
+    return +abs(a);
+  }
+
+  return { f: f, g: g, h: h };
+}
+
+var m = Module({ Math: Math });
+var f = m.f;
+var g = m.g;
+var h = m.h;
+
+assertTrue(isNaN(f(NaN)));
+assertTrue(isNaN(f(undefined)));
+assertTrue(isNaN(f(function() {})));
+
+assertEquals("Infinity", String(1/f(0)));
+assertEquals("Infinity", String(1/f(-0)));
+assertEquals("Infinity", String(f(Infinity)));
+assertEquals("Infinity", String(f(-Infinity)));
+
+assertEquals(0,   f(0));
+assertEquals(0.1, f(0.1));
+assertEquals(0.5, f(0.5));
+assertEquals(0.1, f(-0.1));
+assertEquals(0.5, f(-0.5));
+assertEquals(1,   f(1));
+assertEquals(1.1, f(1.1));
+assertEquals(1.5, f(1.5));
+assertEquals(1,   f(-1));
+assertEquals(1.1, f(-1.1));
+assertEquals(1.5, f(-1.5));
+
+assertEquals(0,          g(0));
+assertEquals(0,          g(0.1));
+assertEquals(0,          g(0.5));
+assertEquals(0,          g(-0.1));
+assertEquals(0,          g(-0.5));
+assertEquals(1,          g(1));
+assertEquals(1,          g(1.1));
+assertEquals(1,          g(1.5));
+assertEquals(4294967295, g(-1));
+assertEquals(4294967295, g(-1.1));
+assertEquals(4294967295, g(-1.5));
+
+assertEquals(0, h(0));
+assertEquals(0, h(0.1));
+assertEquals(0, h(0.5));
+assertEquals(0, h(-0.1));
+assertEquals(0, h(-0.5));
+assertEquals(1, h(1));
+assertEquals(1, h(1.1));
+assertEquals(1, h(1.5));
+assertEquals(1, h(-1));
+assertEquals(1, h(-1.1));
+assertEquals(1, h(-1.5));
+
+assertEquals(Number.MIN_VALUE, f(Number.MIN_VALUE));
+assertEquals(Number.MIN_VALUE, f(-Number.MIN_VALUE));
+assertEquals(Number.MAX_VALUE, f(Number.MAX_VALUE));
+assertEquals(Number.MAX_VALUE, f(-Number.MAX_VALUE));