blob: 41f9c30707b2c3d1de29420b57f25053a1127809 [file] [log] [blame]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001// 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
Ben Murdochb8a8cc12014-11-26 15:28:44 +00005#include "src/compiler/js-builtin-reducer.h"
Emily Bernierd0a1eb72015-03-24 16:35:39 -04006#include "src/compiler/js-graph.h"
Ben Murdochb8a8cc12014-11-26 15:28:44 +00007#include "src/compiler/node-matchers.h"
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00008#include "src/compiler/node-properties.h"
9#include "src/compiler/simplified-operator.h"
10#include "src/objects-inl.h"
Ben Murdoch097c5b22016-05-18 11:27:45 +010011#include "src/type-cache.h"
Ben Murdochb8a8cc12014-11-26 15:28:44 +000012#include "src/types.h"
13
14namespace v8 {
15namespace internal {
16namespace compiler {
17
18
Ben Murdochb8a8cc12014-11-26 15:28:44 +000019// 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;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000029 HeapObjectMatcher m(NodeProperties::GetValueInput(node_, 0));
30 if (!m.HasValue() || !m.Value()->IsJSFunction()) return false;
31 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
Ben Murdochb8a8cc12014-11-26 15:28:44 +000032 return function->shared()->HasBuiltinFunctionId();
33 }
34
35 // Retrieves the BuiltinFunctionId as described above.
36 BuiltinFunctionId GetBuiltinFunctionId() {
37 DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000038 HeapObjectMatcher m(NodeProperties::GetValueInput(node_, 0));
39 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
Ben Murdochb8a8cc12014-11-26 15:28:44 +000040 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 &&
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000049 NodeProperties::GetType(GetJSCallInput(0))->Is(t1);
Ben Murdochb8a8cc12014-11-26 15:28:44 +000050 }
51
52 // Determines whether the call takes two inputs of the given types.
53 bool InputsMatchTwo(Type* t1, Type* t2) {
54 return GetJSCallArity() == 2 &&
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000055 NodeProperties::GetType(GetJSCallInput(0))->Is(t1) &&
56 NodeProperties::GetType(GetJSCallInput(1))->Is(t2);
Ben Murdochb8a8cc12014-11-26 15:28:44 +000057 }
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++) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000062 if (!NodeProperties::GetType(GetJSCallInput(i))->Is(t)) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +000063 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.
Emily Bernierd0a1eb72015-03-24 16:35:39 -040075 return node_->op()->ValueInputCount() - 2;
Ben Murdochb8a8cc12014-11-26 15:28:44 +000076 }
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
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000089JSBuiltinReducer::JSBuiltinReducer(Editor* editor, JSGraph* jsgraph)
Ben Murdoch097c5b22016-05-18 11:27:45 +010090 : AdvancedReducer(editor),
91 jsgraph_(jsgraph),
92 type_cache_(TypeCache::Get()) {}
Ben Murdochb8a8cc12014-11-26 15:28:44 +000093
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++) {
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400109 Node* const input = r.GetJSCallInput(i);
110 value = graph()->NewNode(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000111 common()->Select(MachineRepresentation::kNone),
112 graph()->NewNode(simplified()->NumberLessThan(), input, value), value,
113 input);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000114 }
115 return Replace(value);
116 }
117 return NoChange();
118}
119
Ben Murdochda12d292016-06-02 14:46:10 +0100120// ES6 section 20.2.2.19 Math.imul ( x, y )
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000121Reduction JSBuiltinReducer::ReduceMathImul(Node* node) {
122 JSCallReduction r(node);
Ben Murdochda12d292016-06-02 14:46:10 +0100123 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);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000129 return Replace(value);
130 }
131 return NoChange();
132}
133
Ben Murdochda12d292016-06-02 14:46:10 +0100134// 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}
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000173
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400174// ES6 draft 08-24-14, section 20.2.2.17.
175Reduction 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
Ben Murdoch097c5b22016-05-18 11:27:45 +0100186// ES6 section 20.2.2.28 Math.round ( x )
187Reduction JSBuiltinReducer::ReduceMathRound(Node* node) {
188 JSCallReduction r(node);
Ben Murdochda12d292016-06-02 14:46:10 +0100189 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);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100193 }
Ben Murdochda12d292016-06-02 14:46:10 +0100194 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);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100215 }
216 return NoChange();
217}
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400218
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000219Reduction JSBuiltinReducer::Reduce(Node* node) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000220 Reduction reduction = NoChange();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000221 JSCallReduction r(node);
222
223 // Dispatch according to the BuiltinFunctionId if present.
224 if (!r.HasBuiltinFunctionId()) return NoChange();
225 switch (r.GetBuiltinFunctionId()) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000226 case kMathMax:
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000227 reduction = ReduceMathMax(node);
228 break;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000229 case kMathImul:
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000230 reduction = ReduceMathImul(node);
231 break;
Ben Murdochda12d292016-06-02 14:46:10 +0100232 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;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400241 case kMathFround:
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000242 reduction = ReduceMathFround(node);
243 break;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100244 case kMathRound:
245 reduction = ReduceMathRound(node);
246 break;
Ben Murdochda12d292016-06-02 14:46:10 +0100247 case kMathSqrt:
248 reduction = ReduceMathSqrt(node);
249 break;
250 case kMathTrunc:
251 reduction = ReduceMathTrunc(node);
252 break;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000253 default:
254 break;
255 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000256
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;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000262}
263
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400264
265Graph* JSBuiltinReducer::graph() const { return jsgraph()->graph(); }
266
267
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000268Isolate* JSBuiltinReducer::isolate() const { return jsgraph()->isolate(); }
269
270
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400271CommonOperatorBuilder* JSBuiltinReducer::common() const {
272 return jsgraph()->common();
273}
274
275
276MachineOperatorBuilder* JSBuiltinReducer::machine() const {
277 return jsgraph()->machine();
278}
279
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000280
281SimplifiedOperatorBuilder* JSBuiltinReducer::simplified() const {
282 return jsgraph()->simplified();
283}
284
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000285} // namespace compiler
286} // namespace internal
287} // namespace v8