1// Copyright 2014 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/compiler/graph-inl.h"
6#include "src/compiler/js-builtin-reducer.h"
7#include "src/compiler/node-matchers.h"
8#include "src/compiler/node-properties-inl.h"
9#include "src/types.h"
10
11namespace v8 {
12namespace internal {
13namespace compiler {
14
15
16// Helper method that assumes replacement nodes are pure values that don't
17// produce an effect. Replaces {node} with {reduction} and relaxes effects.
18static Reduction ReplaceWithPureReduction(Node* node, Reduction reduction) {
19  if (reduction.Changed()) {
20    NodeProperties::ReplaceWithValue(node, reduction.replacement());
21    return reduction;
22  }
23  return Reducer::NoChange();
24}
25
26
27// Helper class to access JSCallFunction nodes that are potential candidates
28// for reduction when they have a BuiltinFunctionId associated with them.
29class JSCallReduction {
30 public:
31  explicit JSCallReduction(Node* node) : node_(node) {}
32
33  // Determines whether the node is a JSCallFunction operation that targets a
34  // constant callee being a well-known builtin with a BuiltinFunctionId.
35  bool HasBuiltinFunctionId() {
36    if (node_->opcode() != IrOpcode::kJSCallFunction) return false;
37    HeapObjectMatcher<Object> m(NodeProperties::GetValueInput(node_, 0));
38    if (!m.HasValue() || !m.Value().handle()->IsJSFunction()) return false;
39    Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value().handle());
40    return function->shared()->HasBuiltinFunctionId();
41  }
42
43  // Retrieves the BuiltinFunctionId as described above.
44  BuiltinFunctionId GetBuiltinFunctionId() {
45    DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
46    HeapObjectMatcher<Object> m(NodeProperties::GetValueInput(node_, 0));
47    Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value().handle());
48    return function->shared()->builtin_function_id();
49  }
50
51  // Determines whether the call takes zero inputs.
52  bool InputsMatchZero() { return GetJSCallArity() == 0; }
53
54  // Determines whether the call takes one input of the given type.
55  bool InputsMatchOne(Type* t1) {
56    return GetJSCallArity() == 1 &&
57           NodeProperties::GetBounds(GetJSCallInput(0)).upper->Is(t1);
58  }
59
60  // Determines whether the call takes two inputs of the given types.
61  bool InputsMatchTwo(Type* t1, Type* t2) {
62    return GetJSCallArity() == 2 &&
63           NodeProperties::GetBounds(GetJSCallInput(0)).upper->Is(t1) &&
64           NodeProperties::GetBounds(GetJSCallInput(1)).upper->Is(t2);
65  }
66
67  // Determines whether the call takes inputs all of the given type.
68  bool InputsMatchAll(Type* t) {
69    for (int i = 0; i < GetJSCallArity(); i++) {
70      if (!NodeProperties::GetBounds(GetJSCallInput(i)).upper->Is(t)) {
71        return false;
72      }
73    }
74    return true;
75  }
76
77  Node* left() { return GetJSCallInput(0); }
78  Node* right() { return GetJSCallInput(1); }
79
80  int GetJSCallArity() {
81    DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
82    // Skip first (i.e. callee) and second (i.e. receiver) operand.
83    return OperatorProperties::GetValueInputCount(node_->op()) - 2;
84  }
85
86  Node* GetJSCallInput(int index) {
87    DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
88    DCHECK_LT(index, GetJSCallArity());
89    // Skip first (i.e. callee) and second (i.e. receiver) operand.
90    return NodeProperties::GetValueInput(node_, index + 2);
91  }
92
93 private:
94  Node* node_;
95};
96
97
98// ECMA-262, section 15.8.2.17.
99Reduction JSBuiltinReducer::ReduceMathSqrt(Node* node) {
100  JSCallReduction r(node);
101  if (r.InputsMatchOne(Type::Number())) {
102    // Math.sqrt(a:number) -> Float64Sqrt(a)
103    Node* value = graph()->NewNode(machine()->Float64Sqrt(), r.left());
104    return Replace(value);
105  }
106  return NoChange();
107}
108
109
110// ECMA-262, section 15.8.2.11.
111Reduction JSBuiltinReducer::ReduceMathMax(Node* node) {
112  JSCallReduction r(node);
113  if (r.InputsMatchZero()) {
114    // Math.max() -> -Infinity
115    return Replace(jsgraph()->Constant(-V8_INFINITY));
116  }
117  if (r.InputsMatchOne(Type::Number())) {
118    // Math.max(a:number) -> a
119    return Replace(r.left());
120  }
121  if (r.InputsMatchAll(Type::Integral32())) {
122    // Math.max(a:int32, b:int32, ...)
123    Node* value = r.GetJSCallInput(0);
124    for (int i = 1; i < r.GetJSCallArity(); i++) {
125      Node* p = r.GetJSCallInput(i);
126      Node* control = graph()->start();
127      Node* tag = graph()->NewNode(simplified()->NumberLessThan(), value, p);
128
129      Node* branch = graph()->NewNode(common()->Branch(), tag, control);
130      Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
131      Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
132      Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
133
134      value = graph()->NewNode(common()->Phi(kMachNone, 2), p, value, merge);
135    }
136    return Replace(value);
137  }
138  return NoChange();
139}
140
141
142// ES6 draft 08-24-14, section 20.2.2.19.
143Reduction JSBuiltinReducer::ReduceMathImul(Node* node) {
144  JSCallReduction r(node);
145  if (r.InputsMatchTwo(Type::Integral32(), Type::Integral32())) {
146    // Math.imul(a:int32, b:int32) -> Int32Mul(a, b)
147    Node* value = graph()->NewNode(machine()->Int32Mul(), r.left(), r.right());
148    return Replace(value);
149  }
150  return NoChange();
151}
152
153
154Reduction JSBuiltinReducer::Reduce(Node* node) {
155  JSCallReduction r(node);
156
157  // Dispatch according to the BuiltinFunctionId if present.
158  if (!r.HasBuiltinFunctionId()) return NoChange();
159  switch (r.GetBuiltinFunctionId()) {
160    case kMathSqrt:
161      return ReplaceWithPureReduction(node, ReduceMathSqrt(node));
162    case kMathMax:
163      return ReplaceWithPureReduction(node, ReduceMathMax(node));
164    case kMathImul:
165      return ReplaceWithPureReduction(node, ReduceMathImul(node));
166    default:
167      break;
168  }
169  return NoChange();
170}
171
172}  // namespace compiler
173}  // namespace internal
174}  // namespace v8
175