js-builtin-reducer.cc revision 109988c7ccb6f3fd1a58574fa3dfb88beaef6632
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
121// ES6 draft 08-24-14, section 20.2.2.19.
122Reduction JSBuiltinReducer::ReduceMathImul(Node* node) {
123  JSCallReduction r(node);
124  if (r.InputsMatchTwo(Type::Integral32(), Type::Integral32())) {
125    // Math.imul(a:int32, b:int32) -> Int32Mul(a, b)
126    Node* value = graph()->NewNode(machine()->Int32Mul(), r.left(), r.right());
127    return Replace(value);
128  }
129  return NoChange();
130}
131
132
133// ES6 draft 08-24-14, section 20.2.2.17.
134Reduction JSBuiltinReducer::ReduceMathFround(Node* node) {
135  JSCallReduction r(node);
136  if (r.InputsMatchOne(Type::Number())) {
137    // Math.fround(a:number) -> TruncateFloat64ToFloat32(a)
138    Node* value =
139        graph()->NewNode(machine()->TruncateFloat64ToFloat32(), r.left());
140    return Replace(value);
141  }
142  return NoChange();
143}
144
145// ES6 section 20.2.2.28 Math.round ( x )
146Reduction JSBuiltinReducer::ReduceMathRound(Node* node) {
147  JSCallReduction r(node);
148  if (r.InputsMatchOne(type_cache_.kIntegerOrMinusZeroOrNaN)) {
149    // Math.round(a:integer \/ -0 \/ NaN) -> a
150    return Replace(r.left());
151  }
152  if (r.InputsMatchOne(Type::Number()) &&
153      machine()->Float64RoundUp().IsSupported()) {
154    // Math.round(a:number) -> Select(Float64LessThan(#0.5, Float64Sub(i, a)),
155    //                                Float64Sub(i, #1.0), i)
156    //   where i = Float64RoundUp(a)
157    Node* value = r.left();
158    Node* integer = graph()->NewNode(machine()->Float64RoundUp().op(), value);
159    Node* real = graph()->NewNode(machine()->Float64Sub(), integer, value);
160    return Replace(graph()->NewNode(
161        common()->Select(MachineRepresentation::kFloat64),
162        graph()->NewNode(machine()->Float64LessThan(),
163                         jsgraph()->Float64Constant(0.5), real),
164        graph()->NewNode(machine()->Float64Sub(), integer,
165                         jsgraph()->Float64Constant(1.0)),
166        integer));
167  }
168  return NoChange();
169}
170
171Reduction JSBuiltinReducer::Reduce(Node* node) {
172  Reduction reduction = NoChange();
173  JSCallReduction r(node);
174
175  // Dispatch according to the BuiltinFunctionId if present.
176  if (!r.HasBuiltinFunctionId()) return NoChange();
177  switch (r.GetBuiltinFunctionId()) {
178    case kMathMax:
179      reduction = ReduceMathMax(node);
180      break;
181    case kMathImul:
182      reduction = ReduceMathImul(node);
183      break;
184    case kMathFround:
185      reduction = ReduceMathFround(node);
186      break;
187    case kMathRound:
188      reduction = ReduceMathRound(node);
189      break;
190    default:
191      break;
192  }
193
194  // Replace builtin call assuming replacement nodes are pure values that don't
195  // produce an effect. Replaces {node} with {reduction} and relaxes effects.
196  if (reduction.Changed()) ReplaceWithValue(node, reduction.replacement());
197
198  return reduction;
199}
200
201
202Graph* JSBuiltinReducer::graph() const { return jsgraph()->graph(); }
203
204
205Isolate* JSBuiltinReducer::isolate() const { return jsgraph()->isolate(); }
206
207
208CommonOperatorBuilder* JSBuiltinReducer::common() const {
209  return jsgraph()->common();
210}
211
212
213MachineOperatorBuilder* JSBuiltinReducer::machine() const {
214  return jsgraph()->machine();
215}
216
217
218SimplifiedOperatorBuilder* JSBuiltinReducer::simplified() const {
219  return jsgraph()->simplified();
220}
221
222}  // namespace compiler
223}  // namespace internal
224}  // namespace v8
225