a89f4a32551e8851dee63e44b16f88b1701054b5
[platform/upstream/nodejs.git] / deps / v8 / src / compiler / js-builtin-reducer.cc
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/diamond.h"
6 #include "src/compiler/js-builtin-reducer.h"
7 #include "src/compiler/js-graph.h"
8 #include "src/compiler/node-matchers.h"
9 #include "src/compiler/node-properties.h"
10 #include "src/types.h"
11
12 namespace v8 {
13 namespace internal {
14 namespace compiler {
15
16
17 // Helper method that assumes replacement nodes are pure values that don't
18 // produce an effect. Replaces {node} with {reduction} and relaxes effects.
19 static Reduction ReplaceWithPureReduction(Node* node, Reduction reduction) {
20   if (reduction.Changed()) {
21     NodeProperties::ReplaceWithValue(node, reduction.replacement());
22     return reduction;
23   }
24   return Reducer::NoChange();
25 }
26
27
28 // Helper class to access JSCallFunction nodes that are potential candidates
29 // for reduction when they have a BuiltinFunctionId associated with them.
30 class JSCallReduction {
31  public:
32   explicit JSCallReduction(Node* node) : node_(node) {}
33
34   // Determines whether the node is a JSCallFunction operation that targets a
35   // constant callee being a well-known builtin with a BuiltinFunctionId.
36   bool HasBuiltinFunctionId() {
37     if (node_->opcode() != IrOpcode::kJSCallFunction) return false;
38     HeapObjectMatcher<Object> m(NodeProperties::GetValueInput(node_, 0));
39     if (!m.HasValue() || !m.Value().handle()->IsJSFunction()) return false;
40     Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value().handle());
41     return function->shared()->HasBuiltinFunctionId();
42   }
43
44   // Retrieves the BuiltinFunctionId as described above.
45   BuiltinFunctionId GetBuiltinFunctionId() {
46     DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
47     HeapObjectMatcher<Object> m(NodeProperties::GetValueInput(node_, 0));
48     Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value().handle());
49     return function->shared()->builtin_function_id();
50   }
51
52   // Determines whether the call takes zero inputs.
53   bool InputsMatchZero() { return GetJSCallArity() == 0; }
54
55   // Determines whether the call takes one input of the given type.
56   bool InputsMatchOne(Type* t1) {
57     return GetJSCallArity() == 1 &&
58            NodeProperties::GetBounds(GetJSCallInput(0)).upper->Is(t1);
59   }
60
61   // Determines whether the call takes two inputs of the given types.
62   bool InputsMatchTwo(Type* t1, Type* t2) {
63     return GetJSCallArity() == 2 &&
64            NodeProperties::GetBounds(GetJSCallInput(0)).upper->Is(t1) &&
65            NodeProperties::GetBounds(GetJSCallInput(1)).upper->Is(t2);
66   }
67
68   // Determines whether the call takes inputs all of the given type.
69   bool InputsMatchAll(Type* t) {
70     for (int i = 0; i < GetJSCallArity(); i++) {
71       if (!NodeProperties::GetBounds(GetJSCallInput(i)).upper->Is(t)) {
72         return false;
73       }
74     }
75     return true;
76   }
77
78   Node* left() { return GetJSCallInput(0); }
79   Node* right() { return GetJSCallInput(1); }
80
81   int GetJSCallArity() {
82     DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
83     // Skip first (i.e. callee) and second (i.e. receiver) operand.
84     return node_->op()->ValueInputCount() - 2;
85   }
86
87   Node* GetJSCallInput(int index) {
88     DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
89     DCHECK_LT(index, GetJSCallArity());
90     // Skip first (i.e. callee) and second (i.e. receiver) operand.
91     return NodeProperties::GetValueInput(node_, index + 2);
92   }
93
94  private:
95   Node* node_;
96 };
97
98
99 JSBuiltinReducer::JSBuiltinReducer(JSGraph* jsgraph)
100     : jsgraph_(jsgraph), simplified_(jsgraph->zone()) {}
101
102
103 // ECMA-262, section 15.8.2.1.
104 Reduction JSBuiltinReducer::ReduceMathAbs(Node* node) {
105   JSCallReduction r(node);
106   if (r.InputsMatchOne(Type::Unsigned32())) {
107     // Math.abs(a:uint32) -> a
108     return Replace(r.left());
109   }
110   if (r.InputsMatchOne(Type::Number())) {
111     // Math.abs(a:number) -> (a > 0 ? a : 0 - a)
112     Node* const value = r.left();
113     Node* const zero = jsgraph()->ZeroConstant();
114     return Replace(graph()->NewNode(
115         common()->Select(kMachNone),
116         graph()->NewNode(simplified()->NumberLessThan(), zero, value), value,
117         graph()->NewNode(simplified()->NumberSubtract(), zero, value)));
118   }
119   return NoChange();
120 }
121
122
123 // ECMA-262, section 15.8.2.17.
124 Reduction JSBuiltinReducer::ReduceMathSqrt(Node* node) {
125   JSCallReduction r(node);
126   if (r.InputsMatchOne(Type::Number())) {
127     // Math.sqrt(a:number) -> Float64Sqrt(a)
128     Node* value = graph()->NewNode(machine()->Float64Sqrt(), r.left());
129     return Replace(value);
130   }
131   return NoChange();
132 }
133
134
135 // ECMA-262, section 15.8.2.11.
136 Reduction JSBuiltinReducer::ReduceMathMax(Node* node) {
137   JSCallReduction r(node);
138   if (r.InputsMatchZero()) {
139     // Math.max() -> -Infinity
140     return Replace(jsgraph()->Constant(-V8_INFINITY));
141   }
142   if (r.InputsMatchOne(Type::Number())) {
143     // Math.max(a:number) -> a
144     return Replace(r.left());
145   }
146   if (r.InputsMatchAll(Type::Integral32())) {
147     // Math.max(a:int32, b:int32, ...)
148     Node* value = r.GetJSCallInput(0);
149     for (int i = 1; i < r.GetJSCallArity(); i++) {
150       Node* const input = r.GetJSCallInput(i);
151       value = graph()->NewNode(
152           common()->Select(kMachNone),
153           graph()->NewNode(simplified()->NumberLessThan(), input, value), value,
154           input);
155     }
156     return Replace(value);
157   }
158   return NoChange();
159 }
160
161
162 // ES6 draft 08-24-14, section 20.2.2.19.
163 Reduction JSBuiltinReducer::ReduceMathImul(Node* node) {
164   JSCallReduction r(node);
165   if (r.InputsMatchTwo(Type::Integral32(), Type::Integral32())) {
166     // Math.imul(a:int32, b:int32) -> Int32Mul(a, b)
167     Node* value = graph()->NewNode(machine()->Int32Mul(), r.left(), r.right());
168     return Replace(value);
169   }
170   return NoChange();
171 }
172
173
174 // ES6 draft 08-24-14, section 20.2.2.17.
175 Reduction JSBuiltinReducer::ReduceMathFround(Node* node) {
176   JSCallReduction r(node);
177   if (r.InputsMatchOne(Type::Number())) {
178     // Math.fround(a:number) -> TruncateFloat64ToFloat32(a)
179     Node* value =
180         graph()->NewNode(machine()->TruncateFloat64ToFloat32(), r.left());
181     return Replace(value);
182   }
183   return NoChange();
184 }
185
186
187 // ES6 draft 10-14-14, section 20.2.2.16.
188 Reduction JSBuiltinReducer::ReduceMathFloor(Node* node) {
189   if (!machine()->HasFloat64Floor()) return NoChange();
190   JSCallReduction r(node);
191   if (r.InputsMatchOne(Type::Number())) {
192     // Math.floor(a:number) -> Float64Floor(a)
193     Node* value = graph()->NewNode(machine()->Float64Floor(), r.left());
194     return Replace(value);
195   }
196   return NoChange();
197 }
198
199
200 // ES6 draft 10-14-14, section 20.2.2.10.
201 Reduction JSBuiltinReducer::ReduceMathCeil(Node* node) {
202   if (!machine()->HasFloat64Ceil()) return NoChange();
203   JSCallReduction r(node);
204   if (r.InputsMatchOne(Type::Number())) {
205     // Math.ceil(a:number) -> Float64Ceil(a)
206     Node* value = graph()->NewNode(machine()->Float64Ceil(), r.left());
207     return Replace(value);
208   }
209   return NoChange();
210 }
211
212
213 Reduction JSBuiltinReducer::Reduce(Node* node) {
214   JSCallReduction r(node);
215
216   // Dispatch according to the BuiltinFunctionId if present.
217   if (!r.HasBuiltinFunctionId()) return NoChange();
218   switch (r.GetBuiltinFunctionId()) {
219     case kMathAbs:
220       return ReplaceWithPureReduction(node, ReduceMathAbs(node));
221     case kMathSqrt:
222       return ReplaceWithPureReduction(node, ReduceMathSqrt(node));
223     case kMathMax:
224       return ReplaceWithPureReduction(node, ReduceMathMax(node));
225     case kMathImul:
226       return ReplaceWithPureReduction(node, ReduceMathImul(node));
227     case kMathFround:
228       return ReplaceWithPureReduction(node, ReduceMathFround(node));
229     case kMathFloor:
230       return ReplaceWithPureReduction(node, ReduceMathFloor(node));
231     case kMathCeil:
232       return ReplaceWithPureReduction(node, ReduceMathCeil(node));
233     default:
234       break;
235   }
236   return NoChange();
237 }
238
239
240 Graph* JSBuiltinReducer::graph() const { return jsgraph()->graph(); }
241
242
243 CommonOperatorBuilder* JSBuiltinReducer::common() const {
244   return jsgraph()->common();
245 }
246
247
248 MachineOperatorBuilder* JSBuiltinReducer::machine() const {
249   return jsgraph()->machine();
250 }
251
252 }  // namespace compiler
253 }  // namespace internal
254 }  // namespace v8