| 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 |