blob: 3023031c2f4f82f0a923652cd47198bae98fd512 [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
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
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400133// 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
Ben Murdoch097c5b22016-05-18 11:27:45 +0100145// 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}
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400170
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000171Reduction JSBuiltinReducer::Reduce(Node* node) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000172 Reduction reduction = NoChange();
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000173 JSCallReduction r(node);
174
175 // Dispatch according to the BuiltinFunctionId if present.
176 if (!r.HasBuiltinFunctionId()) return NoChange();
177 switch (r.GetBuiltinFunctionId()) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000178 case kMathMax:
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000179 reduction = ReduceMathMax(node);
180 break;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000181 case kMathImul:
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000182 reduction = ReduceMathImul(node);
183 break;
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400184 case kMathFround:
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000185 reduction = ReduceMathFround(node);
186 break;
Ben Murdoch097c5b22016-05-18 11:27:45 +0100187 case kMathRound:
188 reduction = ReduceMathRound(node);
189 break;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000190 default:
191 break;
192 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000193
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;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000199}
200
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400201
202Graph* JSBuiltinReducer::graph() const { return jsgraph()->graph(); }
203
204
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000205Isolate* JSBuiltinReducer::isolate() const { return jsgraph()->isolate(); }
206
207
Emily Bernierd0a1eb72015-03-24 16:35:39 -0400208CommonOperatorBuilder* JSBuiltinReducer::common() const {
209 return jsgraph()->common();
210}
211
212
213MachineOperatorBuilder* JSBuiltinReducer::machine() const {
214 return jsgraph()->machine();
215}
216
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000217
218SimplifiedOperatorBuilder* JSBuiltinReducer::simplified() const {
219 return jsgraph()->simplified();
220}
221
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000222} // namespace compiler
223} // namespace internal
224} // namespace v8