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.
5 #include "src/compiler/diamond.h"
6 #include "src/compiler/js-builtin-reducer.h"
7 #include "src/compiler/js-graph.h"
8 #include "src/compiler/node-matchers.h"
9 #include "src/compiler/node-properties.h"
10 #include "src/types.h"
17 // Helper method that assumes replacement nodes are pure values that don't
18 // produce an effect. Replaces {node} with {reduction} and relaxes effects.
19 static Reduction ReplaceWithPureReduction(Node* node, Reduction reduction) {
20 if (reduction.Changed()) {
21 NodeProperties::ReplaceWithValue(node, reduction.replacement());
24 return Reducer::NoChange();
28 // Helper class to access JSCallFunction nodes that are potential candidates
29 // for reduction when they have a BuiltinFunctionId associated with them.
30 class JSCallReduction {
32 explicit JSCallReduction(Node* node) : node_(node) {}
34 // Determines whether the node is a JSCallFunction operation that targets a
35 // constant callee being a well-known builtin with a BuiltinFunctionId.
36 bool HasBuiltinFunctionId() {
37 if (node_->opcode() != IrOpcode::kJSCallFunction) return false;
38 HeapObjectMatcher<Object> m(NodeProperties::GetValueInput(node_, 0));
39 if (!m.HasValue() || !m.Value().handle()->IsJSFunction()) return false;
40 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value().handle());
41 return function->shared()->HasBuiltinFunctionId();
44 // Retrieves the BuiltinFunctionId as described above.
45 BuiltinFunctionId GetBuiltinFunctionId() {
46 DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
47 HeapObjectMatcher<Object> m(NodeProperties::GetValueInput(node_, 0));
48 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value().handle());
49 return function->shared()->builtin_function_id();
52 // Determines whether the call takes zero inputs.
53 bool InputsMatchZero() { return GetJSCallArity() == 0; }
55 // Determines whether the call takes one input of the given type.
56 bool InputsMatchOne(Type* t1) {
57 return GetJSCallArity() == 1 &&
58 NodeProperties::GetBounds(GetJSCallInput(0)).upper->Is(t1);
61 // Determines whether the call takes two inputs of the given types.
62 bool InputsMatchTwo(Type* t1, Type* t2) {
63 return GetJSCallArity() == 2 &&
64 NodeProperties::GetBounds(GetJSCallInput(0)).upper->Is(t1) &&
65 NodeProperties::GetBounds(GetJSCallInput(1)).upper->Is(t2);
68 // Determines whether the call takes inputs all of the given type.
69 bool InputsMatchAll(Type* t) {
70 for (int i = 0; i < GetJSCallArity(); i++) {
71 if (!NodeProperties::GetBounds(GetJSCallInput(i)).upper->Is(t)) {
78 Node* left() { return GetJSCallInput(0); }
79 Node* right() { return GetJSCallInput(1); }
81 int GetJSCallArity() {
82 DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
83 // Skip first (i.e. callee) and second (i.e. receiver) operand.
84 return node_->op()->ValueInputCount() - 2;
87 Node* GetJSCallInput(int index) {
88 DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
89 DCHECK_LT(index, GetJSCallArity());
90 // Skip first (i.e. callee) and second (i.e. receiver) operand.
91 return NodeProperties::GetValueInput(node_, index + 2);
99 JSBuiltinReducer::JSBuiltinReducer(JSGraph* jsgraph)
100 : jsgraph_(jsgraph), simplified_(jsgraph->zone()) {}
103 // ECMA-262, section 15.8.2.1.
104 Reduction JSBuiltinReducer::ReduceMathAbs(Node* node) {
105 JSCallReduction r(node);
106 if (r.InputsMatchOne(Type::Unsigned32())) {
107 // Math.abs(a:uint32) -> a
108 return Replace(r.left());
110 if (r.InputsMatchOne(Type::Number())) {
111 // Math.abs(a:number) -> (a > 0 ? a : 0 - a)
112 Node* const value = r.left();
113 Node* const zero = jsgraph()->ZeroConstant();
114 return Replace(graph()->NewNode(
115 common()->Select(kMachNone),
116 graph()->NewNode(simplified()->NumberLessThan(), zero, value), value,
117 graph()->NewNode(simplified()->NumberSubtract(), zero, value)));
123 // ECMA-262, section 15.8.2.17.
124 Reduction JSBuiltinReducer::ReduceMathSqrt(Node* node) {
125 JSCallReduction r(node);
126 if (r.InputsMatchOne(Type::Number())) {
127 // Math.sqrt(a:number) -> Float64Sqrt(a)
128 Node* value = graph()->NewNode(machine()->Float64Sqrt(), r.left());
129 return Replace(value);
135 // ECMA-262, section 15.8.2.11.
136 Reduction JSBuiltinReducer::ReduceMathMax(Node* node) {
137 JSCallReduction r(node);
138 if (r.InputsMatchZero()) {
139 // Math.max() -> -Infinity
140 return Replace(jsgraph()->Constant(-V8_INFINITY));
142 if (r.InputsMatchOne(Type::Number())) {
143 // Math.max(a:number) -> a
144 return Replace(r.left());
146 if (r.InputsMatchAll(Type::Integral32())) {
147 // Math.max(a:int32, b:int32, ...)
148 Node* value = r.GetJSCallInput(0);
149 for (int i = 1; i < r.GetJSCallArity(); i++) {
150 Node* const input = r.GetJSCallInput(i);
151 value = graph()->NewNode(
152 common()->Select(kMachNone),
153 graph()->NewNode(simplified()->NumberLessThan(), input, value), value,
156 return Replace(value);
162 // ES6 draft 08-24-14, section 20.2.2.19.
163 Reduction JSBuiltinReducer::ReduceMathImul(Node* node) {
164 JSCallReduction r(node);
165 if (r.InputsMatchTwo(Type::Integral32(), Type::Integral32())) {
166 // Math.imul(a:int32, b:int32) -> Int32Mul(a, b)
167 Node* value = graph()->NewNode(machine()->Int32Mul(), r.left(), r.right());
168 return Replace(value);
174 // ES6 draft 08-24-14, section 20.2.2.17.
175 Reduction JSBuiltinReducer::ReduceMathFround(Node* node) {
176 JSCallReduction r(node);
177 if (r.InputsMatchOne(Type::Number())) {
178 // Math.fround(a:number) -> TruncateFloat64ToFloat32(a)
180 graph()->NewNode(machine()->TruncateFloat64ToFloat32(), r.left());
181 return Replace(value);
187 // ES6 draft 10-14-14, section 20.2.2.16.
188 Reduction JSBuiltinReducer::ReduceMathFloor(Node* node) {
189 if (!machine()->HasFloat64Floor()) return NoChange();
190 JSCallReduction r(node);
191 if (r.InputsMatchOne(Type::Number())) {
192 // Math.floor(a:number) -> Float64Floor(a)
193 Node* value = graph()->NewNode(machine()->Float64Floor(), r.left());
194 return Replace(value);
200 // ES6 draft 10-14-14, section 20.2.2.10.
201 Reduction JSBuiltinReducer::ReduceMathCeil(Node* node) {
202 if (!machine()->HasFloat64Ceil()) return NoChange();
203 JSCallReduction r(node);
204 if (r.InputsMatchOne(Type::Number())) {
205 // Math.ceil(a:number) -> Float64Ceil(a)
206 Node* value = graph()->NewNode(machine()->Float64Ceil(), r.left());
207 return Replace(value);
213 Reduction JSBuiltinReducer::Reduce(Node* node) {
214 JSCallReduction r(node);
216 // Dispatch according to the BuiltinFunctionId if present.
217 if (!r.HasBuiltinFunctionId()) return NoChange();
218 switch (r.GetBuiltinFunctionId()) {
220 return ReplaceWithPureReduction(node, ReduceMathAbs(node));
222 return ReplaceWithPureReduction(node, ReduceMathSqrt(node));
224 return ReplaceWithPureReduction(node, ReduceMathMax(node));
226 return ReplaceWithPureReduction(node, ReduceMathImul(node));
228 return ReplaceWithPureReduction(node, ReduceMathFround(node));
230 return ReplaceWithPureReduction(node, ReduceMathFloor(node));
232 return ReplaceWithPureReduction(node, ReduceMathCeil(node));
240 Graph* JSBuiltinReducer::graph() const { return jsgraph()->graph(); }
243 CommonOperatorBuilder* JSBuiltinReducer::common() const {
244 return jsgraph()->common();
248 MachineOperatorBuilder* JSBuiltinReducer::machine() const {
249 return jsgraph()->machine();
252 } // namespace compiler
253 } // namespace internal