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/graph-inl.h"
6 #include "src/compiler/js-builtin-reducer.h"
7 #include "src/compiler/node-matchers.h"
8 #include "src/compiler/node-properties-inl.h"
16 // Helper method that assumes replacement nodes are pure values that don't
17 // produce an effect. Replaces {node} with {reduction} and relaxes effects.
18 static Reduction ReplaceWithPureReduction(Node* node, Reduction reduction) {
19 if (reduction.Changed()) {
20 NodeProperties::ReplaceWithValue(node, reduction.replacement());
23 return Reducer::NoChange();
27 // Helper class to access JSCallFunction nodes that are potential candidates
28 // for reduction when they have a BuiltinFunctionId associated with them.
29 class JSCallReduction {
31 explicit JSCallReduction(Node* node) : node_(node) {}
33 // Determines whether the node is a JSCallFunction operation that targets a
34 // constant callee being a well-known builtin with a BuiltinFunctionId.
35 bool HasBuiltinFunctionId() {
36 if (node_->opcode() != IrOpcode::kJSCallFunction) return false;
37 HeapObjectMatcher<Object> m(NodeProperties::GetValueInput(node_, 0));
38 if (!m.HasValue() || !m.Value().handle()->IsJSFunction()) return false;
39 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value().handle());
40 return function->shared()->HasBuiltinFunctionId();
43 // Retrieves the BuiltinFunctionId as described above.
44 BuiltinFunctionId GetBuiltinFunctionId() {
45 DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
46 HeapObjectMatcher<Object> m(NodeProperties::GetValueInput(node_, 0));
47 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value().handle());
48 return function->shared()->builtin_function_id();
51 // Determines whether the call takes zero inputs.
52 bool InputsMatchZero() { return GetJSCallArity() == 0; }
54 // Determines whether the call takes one input of the given type.
55 bool InputsMatchOne(Type* t1) {
56 return GetJSCallArity() == 1 &&
57 NodeProperties::GetBounds(GetJSCallInput(0)).upper->Is(t1);
60 // Determines whether the call takes two inputs of the given types.
61 bool InputsMatchTwo(Type* t1, Type* t2) {
62 return GetJSCallArity() == 2 &&
63 NodeProperties::GetBounds(GetJSCallInput(0)).upper->Is(t1) &&
64 NodeProperties::GetBounds(GetJSCallInput(1)).upper->Is(t2);
67 // Determines whether the call takes inputs all of the given type.
68 bool InputsMatchAll(Type* t) {
69 for (int i = 0; i < GetJSCallArity(); i++) {
70 if (!NodeProperties::GetBounds(GetJSCallInput(i)).upper->Is(t)) {
77 Node* left() { return GetJSCallInput(0); }
78 Node* right() { return GetJSCallInput(1); }
80 int GetJSCallArity() {
81 DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
82 // Skip first (i.e. callee) and second (i.e. receiver) operand.
83 return OperatorProperties::GetValueInputCount(node_->op()) - 2;
86 Node* GetJSCallInput(int index) {
87 DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
88 DCHECK_LT(index, GetJSCallArity());
89 // Skip first (i.e. callee) and second (i.e. receiver) operand.
90 return NodeProperties::GetValueInput(node_, index + 2);
98 // ECMA-262, section 15.8.2.17.
99 Reduction JSBuiltinReducer::ReduceMathSqrt(Node* node) {
100 JSCallReduction r(node);
101 if (r.InputsMatchOne(Type::Number())) {
102 // Math.sqrt(a:number) -> Float64Sqrt(a)
103 Node* value = graph()->NewNode(machine()->Float64Sqrt(), r.left());
104 return Replace(value);
110 // ECMA-262, section 15.8.2.11.
111 Reduction JSBuiltinReducer::ReduceMathMax(Node* node) {
112 JSCallReduction r(node);
113 if (r.InputsMatchZero()) {
114 // Math.max() -> -Infinity
115 return Replace(jsgraph()->Constant(-V8_INFINITY));
117 if (r.InputsMatchOne(Type::Number())) {
118 // Math.max(a:number) -> a
119 return Replace(r.left());
121 if (r.InputsMatchAll(Type::Integral32())) {
122 // Math.max(a:int32, b:int32, ...)
123 Node* value = r.GetJSCallInput(0);
124 for (int i = 1; i < r.GetJSCallArity(); i++) {
125 Node* p = r.GetJSCallInput(i);
126 Node* control = graph()->start();
127 Node* tag = graph()->NewNode(simplified()->NumberLessThan(), value, p);
129 Node* branch = graph()->NewNode(common()->Branch(), tag, control);
130 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
131 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
132 Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
134 value = graph()->NewNode(common()->Phi(kMachNone, 2), p, value, merge);
136 return Replace(value);
142 // ES6 draft 08-24-14, section 20.2.2.19.
143 Reduction JSBuiltinReducer::ReduceMathImul(Node* node) {
144 JSCallReduction r(node);
145 if (r.InputsMatchTwo(Type::Integral32(), Type::Integral32())) {
146 // Math.imul(a:int32, b:int32) -> Int32Mul(a, b)
147 Node* value = graph()->NewNode(machine()->Int32Mul(), r.left(), r.right());
148 return Replace(value);
154 Reduction JSBuiltinReducer::Reduce(Node* node) {
155 JSCallReduction r(node);
157 // Dispatch according to the BuiltinFunctionId if present.
158 if (!r.HasBuiltinFunctionId()) return NoChange();
159 switch (r.GetBuiltinFunctionId()) {
161 return ReplaceWithPureReduction(node, ReduceMathSqrt(node));
163 return ReplaceWithPureReduction(node, ReduceMathMax(node));
165 return ReplaceWithPureReduction(node, ReduceMathImul(node));
172 } // namespace compiler
173 } // namespace internal