OLD | NEW |
1 // Copyright 2015 the V8 project authors. All rights reserved. | 1 // Copyright 2015 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/compiler/js-call-reducer.h" | 5 #include "src/compiler/js-call-reducer.h" |
6 | 6 |
7 #include "src/compiler/js-graph.h" | 7 #include "src/compiler/js-graph.h" |
8 #include "src/compiler/node-matchers.h" | 8 #include "src/compiler/node-matchers.h" |
| 9 #include "src/compiler/simplified-operator.h" |
9 #include "src/objects-inl.h" | 10 #include "src/objects-inl.h" |
10 #include "src/type-feedback-vector-inl.h" | 11 #include "src/type-feedback-vector-inl.h" |
11 | 12 |
12 namespace v8 { | 13 namespace v8 { |
13 namespace internal { | 14 namespace internal { |
14 namespace compiler { | 15 namespace compiler { |
15 | 16 |
16 namespace { | 17 namespace { |
17 | 18 |
18 VectorSlotPair CallCountFeedback(VectorSlotPair p) { | 19 VectorSlotPair CallCountFeedback(VectorSlotPair p) { |
(...skipping 12 matching lines...) Expand all Loading... |
31 TypeFeedbackVector::New(n.GetIsolate(), metadata); | 32 TypeFeedbackVector::New(n.GetIsolate(), metadata); |
32 CallICNexus nexus(vector, slot); | 33 CallICNexus nexus(vector, slot); |
33 nexus.ConfigureMegamorphic(call_count); | 34 nexus.ConfigureMegamorphic(call_count); |
34 return VectorSlotPair(vector, slot); | 35 return VectorSlotPair(vector, slot); |
35 } | 36 } |
36 | 37 |
37 } // namespace | 38 } // namespace |
38 | 39 |
39 | 40 |
40 Reduction JSCallReducer::Reduce(Node* node) { | 41 Reduction JSCallReducer::Reduce(Node* node) { |
41 if (node->opcode() == IrOpcode::kJSCallFunction) { | 42 switch (node->opcode()) { |
42 HeapObjectMatcher m(node->InputAt(0)); | 43 case IrOpcode::kJSCallFunction: |
43 if (m.HasValue() && m.Value()->IsJSFunction()) { | 44 return ReduceJSCallFunction(node); |
44 Handle<SharedFunctionInfo> shared( | 45 default: |
45 Handle<JSFunction>::cast(m.Value())->shared(), isolate()); | 46 break; |
46 if (shared->HasBuiltinFunctionId()) { | |
47 switch (shared->builtin_function_id()) { | |
48 case kFunctionApply: | |
49 return ReduceFunctionPrototypeApply(node); | |
50 case kFunctionCall: | |
51 return ReduceFunctionPrototypeCall(node); | |
52 default: | |
53 break; | |
54 } | |
55 } | |
56 } | |
57 } | 47 } |
58 return NoChange(); | 48 return NoChange(); |
59 } | 49 } |
60 | 50 |
61 | 51 |
62 // ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray ) | 52 // ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray ) |
63 Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) { | 53 Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) { |
64 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); | 54 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); |
65 Node* target = NodeProperties::GetValueInput(node, 0); | 55 Node* target = NodeProperties::GetValueInput(node, 0); |
66 CallFunctionParameters const& p = CallFunctionParametersOf(node->op()); | 56 CallFunctionParameters const& p = CallFunctionParametersOf(node->op()); |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
126 } | 116 } |
127 // Change {node} to the new {JSCallFunction} operator. | 117 // Change {node} to the new {JSCallFunction} operator. |
128 NodeProperties::ChangeOp( | 118 NodeProperties::ChangeOp( |
129 node, javascript()->CallFunction(arity, p.language_mode(), | 119 node, javascript()->CallFunction(arity, p.language_mode(), |
130 CallCountFeedback(p.feedback()), | 120 CallCountFeedback(p.feedback()), |
131 convert_mode, p.tail_call_mode())); | 121 convert_mode, p.tail_call_mode())); |
132 // Change context of {node} to the Function.prototype.apply context, | 122 // Change context of {node} to the Function.prototype.apply context, |
133 // to ensure any exception is thrown in the correct context. | 123 // to ensure any exception is thrown in the correct context. |
134 NodeProperties::ReplaceContextInput( | 124 NodeProperties::ReplaceContextInput( |
135 node, jsgraph()->HeapConstant(handle(apply->context(), isolate()))); | 125 node, jsgraph()->HeapConstant(handle(apply->context(), isolate()))); |
136 return Changed(node); | 126 // Try to further reduce the JSCallFunction {node}. |
| 127 Reduction const reduction = ReduceJSCallFunction(node); |
| 128 return reduction.Changed() ? reduction : Changed(node); |
137 } | 129 } |
138 | 130 |
139 | 131 |
140 // ES6 section 19.2.3.3 Function.prototype.call (thisArg, ...args) | 132 // ES6 section 19.2.3.3 Function.prototype.call (thisArg, ...args) |
141 Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) { | 133 Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) { |
142 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); | 134 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); |
143 CallFunctionParameters const& p = CallFunctionParametersOf(node->op()); | 135 CallFunctionParameters const& p = CallFunctionParametersOf(node->op()); |
144 Handle<JSFunction> call = Handle<JSFunction>::cast( | 136 Handle<JSFunction> call = Handle<JSFunction>::cast( |
145 HeapObjectMatcher(NodeProperties::GetValueInput(node, 0)).Value()); | 137 HeapObjectMatcher(NodeProperties::GetValueInput(node, 0)).Value()); |
146 // Change context of {node} to the Function.prototype.call context, | 138 // Change context of {node} to the Function.prototype.call context, |
(...skipping 14 matching lines...) Expand all Loading... |
161 } else { | 153 } else { |
162 // Just remove the target, which is the first value input. | 154 // Just remove the target, which is the first value input. |
163 convert_mode = ConvertReceiverMode::kAny; | 155 convert_mode = ConvertReceiverMode::kAny; |
164 node->RemoveInput(0); | 156 node->RemoveInput(0); |
165 --arity; | 157 --arity; |
166 } | 158 } |
167 NodeProperties::ChangeOp( | 159 NodeProperties::ChangeOp( |
168 node, javascript()->CallFunction(arity, p.language_mode(), | 160 node, javascript()->CallFunction(arity, p.language_mode(), |
169 CallCountFeedback(p.feedback()), | 161 CallCountFeedback(p.feedback()), |
170 convert_mode, p.tail_call_mode())); | 162 convert_mode, p.tail_call_mode())); |
171 return Changed(node); | 163 // Try to further reduce the JSCallFunction {node}. |
| 164 Reduction const reduction = ReduceJSCallFunction(node); |
| 165 return reduction.Changed() ? reduction : Changed(node); |
172 } | 166 } |
173 | 167 |
174 | 168 |
| 169 Reduction JSCallReducer::ReduceJSCallFunction(Node* node) { |
| 170 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); |
| 171 CallFunctionParameters const& p = CallFunctionParametersOf(node->op()); |
| 172 Node* target = NodeProperties::GetValueInput(node, 0); |
| 173 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); |
| 174 Node* control = NodeProperties::GetControlInput(node); |
| 175 Node* effect = NodeProperties::GetEffectInput(node); |
| 176 |
| 177 // Try to specialize JSCallFunction {node}s with constant {target}s. |
| 178 HeapObjectMatcher m(target); |
| 179 if (m.HasValue()) { |
| 180 if (m.Value()->IsJSFunction()) { |
| 181 Handle<SharedFunctionInfo> shared( |
| 182 Handle<JSFunction>::cast(m.Value())->shared(), isolate()); |
| 183 |
| 184 // Raise a TypeError if the {target} is a "classConstructor". |
| 185 if (IsClassConstructor(shared->kind())) { |
| 186 NodeProperties::RemoveFrameStateInput(node, 0); |
| 187 NodeProperties::RemoveValueInputs(node); |
| 188 NodeProperties::ChangeOp( |
| 189 node, javascript()->CallRuntime( |
| 190 Runtime::kThrowConstructorNonCallableError, 0)); |
| 191 return Changed(node); |
| 192 } |
| 193 |
| 194 // Check for known builtin functions. |
| 195 if (shared->HasBuiltinFunctionId()) { |
| 196 switch (shared->builtin_function_id()) { |
| 197 case kFunctionApply: |
| 198 return ReduceFunctionPrototypeApply(node); |
| 199 case kFunctionCall: |
| 200 return ReduceFunctionPrototypeCall(node); |
| 201 default: |
| 202 break; |
| 203 } |
| 204 } |
| 205 } |
| 206 // Don't mess with other {node}s that have a constant {target}. |
| 207 // TODO(bmeurer): Also support optimizing bound functions and proxies here. |
| 208 return NoChange(); |
| 209 } |
| 210 |
| 211 // Not much we can do if deoptimization support is disabled. |
| 212 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); |
| 213 |
| 214 // Extract feedback from the {node} using the CallICNexus. |
| 215 if (!p.feedback().IsValid()) return NoChange(); |
| 216 CallICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
| 217 Handle<Object> feedback(nexus.GetFeedback(), isolate()); |
| 218 if (feedback->IsWeakCell()) { |
| 219 Handle<WeakCell> cell = Handle<WeakCell>::cast(feedback); |
| 220 if (cell->value()->IsJSFunction()) { |
| 221 // Check that the {target} is still the {target_function}. |
| 222 Node* target_function = jsgraph()->HeapConstant( |
| 223 handle(JSFunction::cast(cell->value()), isolate())); |
| 224 Node* check = graph()->NewNode(simplified()->ReferenceEqual(Type::Any()), |
| 225 target, target_function); |
| 226 Node* branch = |
| 227 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); |
| 228 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
| 229 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, |
| 230 effect, if_false); |
| 231 // TODO(bmeurer): This should be on the AdvancedReducer somehow. |
| 232 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); |
| 233 control = graph()->NewNode(common()->IfTrue(), branch); |
| 234 |
| 235 // Specialize the JSCallFunction node to the {target_function}. |
| 236 NodeProperties::ReplaceValueInput(node, target_function, 0); |
| 237 NodeProperties::ReplaceControlInput(node, control); |
| 238 |
| 239 // Try to further reduce the JSCallFunction {node}. |
| 240 Reduction const reduction = ReduceJSCallFunction(node); |
| 241 return reduction.Changed() ? reduction : Changed(node); |
| 242 } |
| 243 } |
| 244 return NoChange(); |
| 245 } |
| 246 |
| 247 |
175 Graph* JSCallReducer::graph() const { return jsgraph()->graph(); } | 248 Graph* JSCallReducer::graph() const { return jsgraph()->graph(); } |
176 | 249 |
177 | 250 |
178 Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); } | 251 Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); } |
179 | 252 |
180 | 253 |
| 254 CommonOperatorBuilder* JSCallReducer::common() const { |
| 255 return jsgraph()->common(); |
| 256 } |
| 257 |
| 258 |
181 JSOperatorBuilder* JSCallReducer::javascript() const { | 259 JSOperatorBuilder* JSCallReducer::javascript() const { |
182 return jsgraph()->javascript(); | 260 return jsgraph()->javascript(); |
183 } | 261 } |
184 | 262 |
| 263 |
| 264 SimplifiedOperatorBuilder* JSCallReducer::simplified() const { |
| 265 return jsgraph()->simplified(); |
| 266 } |
| 267 |
185 } // namespace compiler | 268 } // namespace compiler |
186 } // namespace internal | 269 } // namespace internal |
187 } // namespace v8 | 270 } // namespace v8 |
OLD | NEW |