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.
4
5 #include "src/compiler/js-builtin-reducer.h"
6 #include "src/compiler/js-graph.h"
7 #include "src/compiler/node-matchers.h"
8 #include "src/compiler/node-properties.h"
9 #include "src/compiler/simplified-operator.h"
10 #include "src/objects-inl.h"
11 #include "src/types.h"
12
13 namespace v8 {
14 namespace internal {
15 namespace compiler {
16
17
18 // Helper class to access JSCallFunction nodes that are potential candidates
19 // for reduction when they have a BuiltinFunctionId associated with them.
20 class JSCallReduction {
21 public:
JSCallReduction(Node * node)22 explicit JSCallReduction(Node* node) : node_(node) {}
23
24 // Determines whether the node is a JSCallFunction operation that targets a
25 // constant callee being a well-known builtin with a BuiltinFunctionId.
HasBuiltinFunctionId()26 bool HasBuiltinFunctionId() {
27 if (node_->opcode() != IrOpcode::kJSCallFunction) return false;
28 HeapObjectMatcher m(NodeProperties::GetValueInput(node_, 0));
29 if (!m.HasValue() || !m.Value()->IsJSFunction()) return false;
30 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
31 return function->shared()->HasBuiltinFunctionId();
32 }
33
34 // Retrieves the BuiltinFunctionId as described above.
GetBuiltinFunctionId()35 BuiltinFunctionId GetBuiltinFunctionId() {
36 DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
37 HeapObjectMatcher m(NodeProperties::GetValueInput(node_, 0));
38 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
39 return function->shared()->builtin_function_id();
40 }
41
42 // Determines whether the call takes zero inputs.
InputsMatchZero()43 bool InputsMatchZero() { return GetJSCallArity() == 0; }
44
45 // Determines whether the call takes one input of the given type.
InputsMatchOne(Type * t1)46 bool InputsMatchOne(Type* t1) {
47 return GetJSCallArity() == 1 &&
48 NodeProperties::GetType(GetJSCallInput(0))->Is(t1);
49 }
50
51 // Determines whether the call takes two inputs of the given types.
InputsMatchTwo(Type * t1,Type * t2)52 bool InputsMatchTwo(Type* t1, Type* t2) {
53 return GetJSCallArity() == 2 &&
54 NodeProperties::GetType(GetJSCallInput(0))->Is(t1) &&
55 NodeProperties::GetType(GetJSCallInput(1))->Is(t2);
56 }
57
58 // Determines whether the call takes inputs all of the given type.
InputsMatchAll(Type * t)59 bool InputsMatchAll(Type* t) {
60 for (int i = 0; i < GetJSCallArity(); i++) {
61 if (!NodeProperties::GetType(GetJSCallInput(i))->Is(t)) {
62 return false;
63 }
64 }
65 return true;
66 }
67
left()68 Node* left() { return GetJSCallInput(0); }
right()69 Node* right() { return GetJSCallInput(1); }
70
GetJSCallArity()71 int GetJSCallArity() {
72 DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
73 // Skip first (i.e. callee) and second (i.e. receiver) operand.
74 return node_->op()->ValueInputCount() - 2;
75 }
76
GetJSCallInput(int index)77 Node* GetJSCallInput(int index) {
78 DCHECK_EQ(IrOpcode::kJSCallFunction, node_->opcode());
79 DCHECK_LT(index, GetJSCallArity());
80 // Skip first (i.e. callee) and second (i.e. receiver) operand.
81 return NodeProperties::GetValueInput(node_, index + 2);
82 }
83
84 private:
85 Node* node_;
86 };
87
88
JSBuiltinReducer(Editor * editor,JSGraph * jsgraph)89 JSBuiltinReducer::JSBuiltinReducer(Editor* editor, JSGraph* jsgraph)
90 : AdvancedReducer(editor), jsgraph_(jsgraph) {}
91
92
93 // ECMA-262, section 15.8.2.11.
ReduceMathMax(Node * node)94 Reduction JSBuiltinReducer::ReduceMathMax(Node* node) {
95 JSCallReduction r(node);
96 if (r.InputsMatchZero()) {
97 // Math.max() -> -Infinity
98 return Replace(jsgraph()->Constant(-V8_INFINITY));
99 }
100 if (r.InputsMatchOne(Type::Number())) {
101 // Math.max(a:number) -> a
102 return Replace(r.left());
103 }
104 if (r.InputsMatchAll(Type::Integral32())) {
105 // Math.max(a:int32, b:int32, ...)
106 Node* value = r.GetJSCallInput(0);
107 for (int i = 1; i < r.GetJSCallArity(); i++) {
108 Node* const input = r.GetJSCallInput(i);
109 value = graph()->NewNode(
110 common()->Select(MachineRepresentation::kNone),
111 graph()->NewNode(simplified()->NumberLessThan(), input, value), value,
112 input);
113 }
114 return Replace(value);
115 }
116 return NoChange();
117 }
118
119
120 // ES6 draft 08-24-14, section 20.2.2.19.
ReduceMathImul(Node * node)121 Reduction JSBuiltinReducer::ReduceMathImul(Node* node) {
122 JSCallReduction r(node);
123 if (r.InputsMatchTwo(Type::Integral32(), Type::Integral32())) {
124 // Math.imul(a:int32, b:int32) -> Int32Mul(a, b)
125 Node* value = graph()->NewNode(machine()->Int32Mul(), r.left(), r.right());
126 return Replace(value);
127 }
128 return NoChange();
129 }
130
131
132 // ES6 draft 08-24-14, section 20.2.2.17.
ReduceMathFround(Node * node)133 Reduction JSBuiltinReducer::ReduceMathFround(Node* node) {
134 JSCallReduction r(node);
135 if (r.InputsMatchOne(Type::Number())) {
136 // Math.fround(a:number) -> TruncateFloat64ToFloat32(a)
137 Node* value =
138 graph()->NewNode(machine()->TruncateFloat64ToFloat32(), r.left());
139 return Replace(value);
140 }
141 return NoChange();
142 }
143
144
Reduce(Node * node)145 Reduction JSBuiltinReducer::Reduce(Node* node) {
146 Reduction reduction = NoChange();
147 JSCallReduction r(node);
148
149 // Dispatch according to the BuiltinFunctionId if present.
150 if (!r.HasBuiltinFunctionId()) return NoChange();
151 switch (r.GetBuiltinFunctionId()) {
152 case kMathMax:
153 reduction = ReduceMathMax(node);
154 break;
155 case kMathImul:
156 reduction = ReduceMathImul(node);
157 break;
158 case kMathFround:
159 reduction = ReduceMathFround(node);
160 break;
161 default:
162 break;
163 }
164
165 // Replace builtin call assuming replacement nodes are pure values that don't
166 // produce an effect. Replaces {node} with {reduction} and relaxes effects.
167 if (reduction.Changed()) ReplaceWithValue(node, reduction.replacement());
168
169 return reduction;
170 }
171
172
graph() const173 Graph* JSBuiltinReducer::graph() const { return jsgraph()->graph(); }
174
175
isolate() const176 Isolate* JSBuiltinReducer::isolate() const { return jsgraph()->isolate(); }
177
178
common() const179 CommonOperatorBuilder* JSBuiltinReducer::common() const {
180 return jsgraph()->common();
181 }
182
183
machine() const184 MachineOperatorBuilder* JSBuiltinReducer::machine() const {
185 return jsgraph()->machine();
186 }
187
188
simplified() const189 SimplifiedOperatorBuilder* JSBuiltinReducer::simplified() const {
190 return jsgraph()->simplified();
191 }
192
193 } // namespace compiler
194 } // namespace internal
195 } // namespace v8
196