OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "src/compiler/js-call-reducer.h" |
| 6 |
| 7 #include "src/compiler/js-graph.h" |
| 8 #include "src/compiler/node-matchers.h" |
| 9 #include "src/objects-inl.h" |
| 10 #include "src/type-feedback-vector-inl.h" |
| 11 |
| 12 namespace v8 { |
| 13 namespace internal { |
| 14 namespace compiler { |
| 15 |
| 16 namespace { |
| 17 |
| 18 VectorSlotPair CallCountFeedback(VectorSlotPair p) { |
| 19 // Extract call count from {p}. |
| 20 if (!p.IsValid()) return VectorSlotPair(); |
| 21 CallICNexus n(p.vector(), p.slot()); |
| 22 int const call_count = n.ExtractCallCount(); |
| 23 if (call_count <= 0) return VectorSlotPair(); |
| 24 |
| 25 // Create megamorphic CallIC feedback with the given {call_count}. |
| 26 StaticFeedbackVectorSpec spec; |
| 27 FeedbackVectorSlot slot = spec.AddCallICSlot(); |
| 28 Handle<TypeFeedbackMetadata> metadata = |
| 29 TypeFeedbackMetadata::New(n.GetIsolate(), &spec); |
| 30 Handle<TypeFeedbackVector> vector = |
| 31 TypeFeedbackVector::New(n.GetIsolate(), metadata); |
| 32 CallICNexus nexus(vector, slot); |
| 33 nexus.ConfigureMegamorphic(call_count); |
| 34 return VectorSlotPair(vector, slot); |
| 35 } |
| 36 |
| 37 } // namespace |
| 38 |
| 39 |
| 40 Reduction JSCallReducer::Reduce(Node* node) { |
| 41 if (node->opcode() == IrOpcode::kJSCallFunction) { |
| 42 HeapObjectMatcher m(node->InputAt(0)); |
| 43 if (m.HasValue() && m.Value()->IsJSFunction()) { |
| 44 Handle<SharedFunctionInfo> shared( |
| 45 Handle<JSFunction>::cast(m.Value())->shared(), isolate()); |
| 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 } |
| 58 return NoChange(); |
| 59 } |
| 60 |
| 61 |
| 62 // ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray ) |
| 63 Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) { |
| 64 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); |
| 65 Node* target = NodeProperties::GetValueInput(node, 0); |
| 66 CallFunctionParameters const& p = CallFunctionParametersOf(node->op()); |
| 67 Handle<JSFunction> apply = |
| 68 Handle<JSFunction>::cast(HeapObjectMatcher(target).Value()); |
| 69 size_t arity = p.arity(); |
| 70 DCHECK_LE(2u, arity); |
| 71 ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny; |
| 72 if (arity == 2) { |
| 73 // Neither thisArg nor argArray was provided. |
| 74 convert_mode = ConvertReceiverMode::kNullOrUndefined; |
| 75 node->ReplaceInput(0, node->InputAt(1)); |
| 76 node->ReplaceInput(1, jsgraph()->UndefinedConstant()); |
| 77 } else if (arity == 3) { |
| 78 // The argArray was not provided, just remove the {target}. |
| 79 node->RemoveInput(0); |
| 80 --arity; |
| 81 } else if (arity == 4) { |
| 82 // Check if argArray is an arguments object, and {node} is the only value |
| 83 // user of argArray (except for value uses in frame states). |
| 84 Node* arg_array = NodeProperties::GetValueInput(node, 3); |
| 85 if (arg_array->opcode() != IrOpcode::kJSCreateArguments) return NoChange(); |
| 86 for (Edge edge : arg_array->use_edges()) { |
| 87 if (edge.from()->opcode() == IrOpcode::kStateValues) continue; |
| 88 if (!NodeProperties::IsValueEdge(edge)) continue; |
| 89 if (edge.from() == node) continue; |
| 90 return NoChange(); |
| 91 } |
| 92 // Get to the actual frame state from which to extract the arguments; |
| 93 // we can only optimize this in case the {node} was already inlined into |
| 94 // some other function (and same for the {arg_array}). |
| 95 CreateArgumentsParameters const& p = |
| 96 CreateArgumentsParametersOf(arg_array->op()); |
| 97 Node* frame_state = NodeProperties::GetFrameStateInput(arg_array, 0); |
| 98 Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput); |
| 99 if (outer_state->opcode() != IrOpcode::kFrameState) return NoChange(); |
| 100 FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state); |
| 101 if (outer_info.type() == FrameStateType::kArgumentsAdaptor) { |
| 102 // Need to take the parameters from the arguments adaptor. |
| 103 frame_state = outer_state; |
| 104 } |
| 105 FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state); |
| 106 if (p.type() == CreateArgumentsParameters::kMappedArguments) { |
| 107 // Mapped arguments (sloppy mode) cannot be handled if they are aliased. |
| 108 Handle<SharedFunctionInfo> shared; |
| 109 if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); |
| 110 if (shared->internal_formal_parameter_count() != 0) return NoChange(); |
| 111 } |
| 112 // Remove the argArray input from the {node}. |
| 113 node->RemoveInput(static_cast<int>(--arity)); |
| 114 // Add the actual parameters to the {node}, skipping the receiver. |
| 115 Node* const parameters = frame_state->InputAt(kFrameStateParametersInput); |
| 116 for (int i = p.start_index() + 1; i < state_info.parameter_count(); ++i) { |
| 117 node->InsertInput(graph()->zone(), static_cast<int>(arity), |
| 118 parameters->InputAt(i)); |
| 119 ++arity; |
| 120 } |
| 121 // Drop the {target} from the {node}. |
| 122 node->RemoveInput(0); |
| 123 --arity; |
| 124 } else { |
| 125 return NoChange(); |
| 126 } |
| 127 // Change {node} to the new {JSCallFunction} operator. |
| 128 NodeProperties::ChangeOp( |
| 129 node, javascript()->CallFunction(arity, p.language_mode(), |
| 130 CallCountFeedback(p.feedback()), |
| 131 convert_mode, p.tail_call_mode())); |
| 132 // Change context of {node} to the Function.prototype.apply context, |
| 133 // to ensure any exception is thrown in the correct context. |
| 134 NodeProperties::ReplaceContextInput( |
| 135 node, jsgraph()->HeapConstant(handle(apply->context(), isolate()))); |
| 136 return Changed(node); |
| 137 } |
| 138 |
| 139 |
| 140 // ES6 section 19.2.3.3 Function.prototype.call (thisArg, ...args) |
| 141 Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) { |
| 142 DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); |
| 143 CallFunctionParameters const& p = CallFunctionParametersOf(node->op()); |
| 144 Handle<JSFunction> call = Handle<JSFunction>::cast( |
| 145 HeapObjectMatcher(NodeProperties::GetValueInput(node, 0)).Value()); |
| 146 // Change context of {node} to the Function.prototype.call context, |
| 147 // to ensure any exception is thrown in the correct context. |
| 148 NodeProperties::ReplaceContextInput( |
| 149 node, jsgraph()->HeapConstant(handle(call->context(), isolate()))); |
| 150 // Remove the target from {node} and use the receiver as target instead, and |
| 151 // the thisArg becomes the new target. If thisArg was not provided, insert |
| 152 // undefined instead. |
| 153 size_t arity = p.arity(); |
| 154 DCHECK_LE(2u, arity); |
| 155 ConvertReceiverMode convert_mode; |
| 156 if (arity == 2) { |
| 157 // The thisArg was not provided, use undefined as receiver. |
| 158 convert_mode = ConvertReceiverMode::kNullOrUndefined; |
| 159 node->ReplaceInput(0, node->InputAt(1)); |
| 160 node->ReplaceInput(1, jsgraph()->UndefinedConstant()); |
| 161 } else { |
| 162 // Just remove the target, which is the first value input. |
| 163 convert_mode = ConvertReceiverMode::kAny; |
| 164 node->RemoveInput(0); |
| 165 --arity; |
| 166 } |
| 167 NodeProperties::ChangeOp( |
| 168 node, javascript()->CallFunction(arity, p.language_mode(), |
| 169 CallCountFeedback(p.feedback()), |
| 170 convert_mode, p.tail_call_mode())); |
| 171 return Changed(node); |
| 172 } |
| 173 |
| 174 |
| 175 Graph* JSCallReducer::graph() const { return jsgraph()->graph(); } |
| 176 |
| 177 |
| 178 Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); } |
| 179 |
| 180 |
| 181 JSOperatorBuilder* JSCallReducer::javascript() const { |
| 182 return jsgraph()->javascript(); |
| 183 } |
| 184 |
| 185 } // namespace compiler |
| 186 } // namespace internal |
| 187 } // namespace v8 |
OLD | NEW |