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