js-builtin-reducer.cc revision bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8
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/js-builtin-reducer.h"
6#include "src/compiler/js-graph.h"
7#include "src/compiler/node-matchers.h"
8#include "src/compiler/node-properties.h"
9#include "src/compiler/simplified-operator.h"
10#include "src/objects-inl.h"
11#include "src/type-cache.h"
12#include "src/types.h"
13
14namespace v8 {
15namespace internal {
16namespace compiler {
17
18
19// Helper class to access JSCallFunction nodes that are potential candidates
20// for reduction when they have a BuiltinFunctionId associated with them.
21class JSCallReduction {
22 public:
23  explicit JSCallReduction(Node* node) : node_(node) {}
24
25  // Determines whether the node is a JSCallFunction operation that targets a
26  // constant callee being a well-known builtin with a BuiltinFunctionId.
27  bool HasBuiltinFunctionId() {
28    if (node_->opcode() != IrOpcode::kJSCallFunction) return false;
29    HeapObjectMatcher m(NodeProperties::GetValueInput(node_, 0));
30    if (!m.HasValue() || !m.Value()->IsJSFunction()) return false;
31    Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
32    return function->shared()->HasBuiltinFunctionId();
33  }
34
35  // Retrieves the BuiltinFunctionId as described above.
36  BuiltinFunctionId GetBuiltinFunctionId() {
37    DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
38    HeapObjectMatcher m(NodeProperties::GetValueInput(node_, 0));
39    Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
40    return function->shared()->builtin_function_id();
41  }
42
43  // Determines whether the call takes zero inputs.
44  bool InputsMatchZero() { return GetJSCallArity() == 0; }
45
46  // Determines whether the call takes one input of the given type.
47  bool InputsMatchOne(Type* t1) {
48    return GetJSCallArity() == 1 &&
49           NodeProperties::GetType(GetJSCallInput(0))->Is(t1);
50  }
51
52  // Determines whether the call takes two inputs of the given types.
53  bool InputsMatchTwo(Type* t1, Type* t2) {
54    return GetJSCallArity() == 2 &&
55           NodeProperties::GetType(GetJSCallInput(0))->Is(t1) &&
56           NodeProperties::GetType(GetJSCallInput(1))->Is(t2);
57  }
58
59  // Determines whether the call takes inputs all of the given type.
60  bool InputsMatchAll(Type* t) {
61    for (int i = 0; i < GetJSCallArity(); i++) {
62      if (!NodeProperties::GetType(GetJSCallInput(i))->Is(t)) {
63        return false;
64      }
65    }
66    return true;
67  }
68
69  Node* left() { return GetJSCallInput(0); }
70  Node* right() { return GetJSCallInput(1); }
71
72  int GetJSCallArity() {
73    DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
74    // Skip first (i.e. callee) and second (i.e. receiver) operand.
75    return node_->op()->ValueInputCount() - 2;
76  }
77
78  Node* GetJSCallInput(int index) {
79    DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
80    DCHECK_LT(index, GetJSCallArity());
81    // Skip first (i.e. callee) and second (i.e. receiver) operand.
82    return NodeProperties::GetValueInput(node_, index + 2);
83  }
84
85 private:
86  Node* node_;
87};
88
89JSBuiltinReducer::JSBuiltinReducer(Editor* editor, JSGraph* jsgraph)
90    : AdvancedReducer(editor),
91      jsgraph_(jsgraph),
92      type_cache_(TypeCache::Get()) {}
93
94// ECMA-262, section 15.8.2.11.
95Reduction JSBuiltinReducer::ReduceMathMax(Node* node) {
96  JSCallReduction r(node);
97  if (r.InputsMatchZero()) {
98    // Math.max() -> -Infinity
99    return Replace(jsgraph()->Constant(-V8_INFINITY));
100  }
101  if (r.InputsMatchOne(Type::Number())) {
102    // Math.max(a:number) -> a
103    return Replace(r.left());
104  }
105  if (r.InputsMatchAll(Type::Integral32())) {
106    // Math.max(a:int32, b:int32, ...)
107    Node* value = r.GetJSCallInput(0);
108    for (int i = 1; i < r.GetJSCallArity(); i++) {
109      Node* const input = r.GetJSCallInput(i);
110      value = graph()->NewNode(
111          common()->Select(MachineRepresentation::kNone),
112          graph()->NewNode(simplified()->NumberLessThan(), input, value), value,
113          input);
114    }
115    return Replace(value);
116  }
117  return NoChange();
118}
119
120// ES6 section 20.2.2.19 Math.imul ( x, y )
121Reduction JSBuiltinReducer::ReduceMathImul(Node* node) {
122  JSCallReduction r(node);
123  if (r.InputsMatchTwo(Type::Number(), Type::Number())) {
124    // Math.imul(a:number, b:number) -> NumberImul(NumberToUint32(a),
125    //                                             NumberToUint32(b))
126    Node* a = graph()->NewNode(simplified()->NumberToUint32(), r.left());
127    Node* b = graph()->NewNode(simplified()->NumberToUint32(), r.right());
128    Node* value = graph()->NewNode(simplified()->NumberImul(), a, b);
129    return Replace(value);
130  }
131  return NoChange();
132}
133
134// ES6 section 20.2.2.10 Math.ceil ( x )
135Reduction JSBuiltinReducer::ReduceMathCeil(Node* node) {
136  JSCallReduction r(node);
137  if (r.InputsMatchOne(Type::Number())) {
138    // Math.ceil(a:number) -> NumberCeil(a)
139    Node* value = graph()->NewNode(simplified()->NumberCeil(), r.left());
140    return Replace(value);
141  }
142  return NoChange();
143}
144
145// ES6 section 20.2.2.11 Math.clz32 ( x )
146Reduction JSBuiltinReducer::ReduceMathClz32(Node* node) {
147  JSCallReduction r(node);
148  if (r.InputsMatchOne(Type::Unsigned32())) {
149    // Math.clz32(a:unsigned32) -> NumberClz32(a)
150    Node* value = graph()->NewNode(simplified()->NumberClz32(), r.left());
151    return Replace(value);
152  }
153  if (r.InputsMatchOne(Type::Number())) {
154    // Math.clz32(a:number) -> NumberClz32(NumberToUint32(a))
155    Node* value = graph()->NewNode(
156        simplified()->NumberClz32(),
157        graph()->NewNode(simplified()->NumberToUint32(), r.left()));
158    return Replace(value);
159  }
160  return NoChange();
161}
162
163// ES6 draft 08-24-14, section 20.2.2.16.
164Reduction JSBuiltinReducer::ReduceMathFloor(Node* node) {
165  JSCallReduction r(node);
166  if (r.InputsMatchOne(Type::Number())) {
167    // Math.floor(a:number) -> NumberFloor(a)
168    Node* value = graph()->NewNode(simplified()->NumberFloor(), r.left());
169    return Replace(value);
170  }
171  return NoChange();
172}
173
174// ES6 draft 08-24-14, section 20.2.2.17.
175Reduction JSBuiltinReducer::ReduceMathFround(Node* node) {
176  JSCallReduction r(node);
177  if (r.InputsMatchOne(Type::NumberOrUndefined())) {
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// ES6 section 20.2.2.28 Math.round ( x )
187Reduction JSBuiltinReducer::ReduceMathRound(Node* node) {
188  JSCallReduction r(node);
189  if (r.InputsMatchOne(Type::Number())) {
190    // Math.round(a:number) -> NumberRound(a)
191    Node* value = graph()->NewNode(simplified()->NumberRound(), r.left());
192    return Replace(value);
193  }
194  return NoChange();
195}
196
197// ES6 section 20.2.2.32 Math.sqrt ( x )
198Reduction JSBuiltinReducer::ReduceMathSqrt(Node* node) {
199  JSCallReduction r(node);
200  if (r.InputsMatchOne(Type::Number())) {
201    // Math.sqrt(a:number) -> Float64Sqrt(a)
202    Node* value = graph()->NewNode(machine()->Float64Sqrt(), r.left());
203    return Replace(value);
204  }
205  return NoChange();
206}
207
208// ES6 section 20.2.2.35 Math.trunc ( x )
209Reduction JSBuiltinReducer::ReduceMathTrunc(Node* node) {
210  JSCallReduction r(node);
211  if (r.InputsMatchOne(Type::Number())) {
212    // Math.trunc(a:number) -> NumberTrunc(a)
213    Node* value = graph()->NewNode(simplified()->NumberTrunc(), r.left());
214    return Replace(value);
215  }
216  return NoChange();
217}
218
219Reduction JSBuiltinReducer::Reduce(Node* node) {
220  Reduction reduction = NoChange();
221  JSCallReduction r(node);
222
223  // Dispatch according to the BuiltinFunctionId if present.
224  if (!r.HasBuiltinFunctionId()) return NoChange();
225  switch (r.GetBuiltinFunctionId()) {
226    case kMathMax:
227      reduction = ReduceMathMax(node);
228      break;
229    case kMathImul:
230      reduction = ReduceMathImul(node);
231      break;
232    case kMathClz32:
233      reduction = ReduceMathClz32(node);
234      break;
235    case kMathCeil:
236      reduction = ReduceMathCeil(node);
237      break;
238    case kMathFloor:
239      reduction = ReduceMathFloor(node);
240      break;
241    case kMathFround:
242      reduction = ReduceMathFround(node);
243      break;
244    case kMathRound:
245      reduction = ReduceMathRound(node);
246      break;
247    case kMathSqrt:
248      reduction = ReduceMathSqrt(node);
249      break;
250    case kMathTrunc:
251      reduction = ReduceMathTrunc(node);
252      break;
253    default:
254      break;
255  }
256
257  // Replace builtin call assuming replacement nodes are pure values that don't
258  // produce an effect. Replaces {node} with {reduction} and relaxes effects.
259  if (reduction.Changed()) ReplaceWithValue(node, reduction.replacement());
260
261  return reduction;
262}
263
264
265Graph* JSBuiltinReducer::graph() const { return jsgraph()->graph(); }
266
267
268Isolate* JSBuiltinReducer::isolate() const { return jsgraph()->isolate(); }
269
270
271CommonOperatorBuilder* JSBuiltinReducer::common() const {
272  return jsgraph()->common();
273}
274
275
276MachineOperatorBuilder* JSBuiltinReducer::machine() const {
277  return jsgraph()->machine();
278}
279
280
281SimplifiedOperatorBuilder* JSBuiltinReducer::simplified() const {
282  return jsgraph()->simplified();
283}
284
285}  // namespace compiler
286}  // namespace internal
287}  // namespace v8
288