5 #include "src/compiler/js-inlining-heuristic.h" 7 #include "src/compiler/common-operator.h" 8 #include "src/compiler/compiler-source-position-table.h" 9 #include "src/compiler/node-matchers.h" 10 #include "src/compiler/simplified-operator.h" 11 #include "src/objects-inl.h" 12 #include "src/optimized-compilation-info.h" 20 if (FLAG_trace_turbo_inlining) PrintF(__VA_ARGS__); \ 25 int CollectFunctions(Node* node, Handle<JSFunction>* functions,
26 Handle<BytecodeArray>* bytecode,
int functions_size,
27 Handle<SharedFunctionInfo>& shared, Isolate* isolate) {
28 DCHECK_NE(0, functions_size);
29 HeapObjectMatcher m(node);
30 if (m.HasValue() && m.Value()->IsJSFunction()) {
31 functions[0] = Handle<JSFunction>::cast(m.Value());
32 if (functions[0]->shared()->HasBytecodeArray()) {
33 bytecode[0] = handle(functions[0]->shared()->GetBytecodeArray(), isolate);
38 int const value_input_count = m.node()->op()->ValueInputCount();
39 if (value_input_count > functions_size)
return 0;
40 for (
int n = 0; n < value_input_count; ++n) {
41 HeapObjectMatcher m(node->InputAt(n));
42 if (!m.HasValue() || !m.Value()->IsJSFunction())
return 0;
43 functions[n] = Handle<JSFunction>::cast(m.Value());
44 if (functions[n]->shared()->HasBytecodeArray()) {
46 handle(functions[n]->shared()->GetBytecodeArray(), isolate);
49 return value_input_count;
51 if (m.IsJSCreateClosure()) {
52 CreateClosureParameters
const& p = CreateClosureParametersOf(m.op());
53 functions[0] = Handle<JSFunction>::null();
54 shared = p.shared_info();
55 if (shared->HasBytecodeArray()) {
56 bytecode[0] = handle(shared->GetBytecodeArray(), isolate);
63 bool CanInlineFunction(Handle<SharedFunctionInfo> shared,
64 Handle<BytecodeArray> bytecode) {
66 if (shared->HasBuiltinFunctionId())
return false;
69 if (!shared->IsUserJavaScript())
return false;
74 if (bytecode.is_null())
return false;
77 if (bytecode->length() > FLAG_max_inlined_bytecode_size) {
84 bool IsSmallInlineFunction(Handle<BytecodeArray> bytecode) {
87 if (!bytecode.is_null() &&
88 bytecode->length() <= FLAG_max_inlined_bytecode_size_small) {
96 Reduction JSInliningHeuristic::Reduce(Node* node) {
97 if (!IrOpcode::IsInlineeOpcode(node->opcode()))
return NoChange();
100 if (seen_.find(node->id()) != seen_.end())
return NoChange();
101 seen_.insert(node->id());
104 Node* callee = node->InputAt(0);
106 candidate.node = node;
107 candidate.num_functions =
108 CollectFunctions(callee, candidate.functions, candidate.bytecode,
109 kMaxCallPolymorphism, candidate.shared_info, isolate());
110 if (candidate.num_functions == 0) {
112 }
else if (candidate.num_functions > 1 && !FLAG_polymorphic_inlining) {
114 "Not considering call site #%d:%s, because polymorphic inlining " 116 node->id(), node->op()->mnemonic());
120 bool can_inline =
false, small_inline =
true;
121 candidate.total_size = 0;
122 Node* frame_state = NodeProperties::GetFrameStateInput(node);
123 FrameStateInfo
const& frame_info = FrameStateInfoOf(frame_state->op());
124 Handle<SharedFunctionInfo> frame_shared_info;
125 for (
int i = 0;
i < candidate.num_functions; ++
i) {
126 Handle<SharedFunctionInfo> shared =
127 candidate.functions[
i].is_null()
128 ? candidate.shared_info
129 : handle(candidate.functions[
i]->shared(), isolate());
130 Handle<BytecodeArray> bytecode = candidate.bytecode[
i];
131 candidate.can_inline_function[
i] = CanInlineFunction(shared, bytecode);
141 if (frame_info.shared_info().ToHandle(&frame_shared_info) &&
142 *frame_shared_info == *shared) {
143 TRACE(
"Not considering call site #%d:%s, because of recursive inlining\n",
144 node->id(), node->op()->mnemonic());
145 candidate.can_inline_function[
i] =
false;
147 if (candidate.can_inline_function[
i]) {
149 candidate.total_size += bytecode->length();
151 if (!IsSmallInlineFunction(bytecode)) {
152 small_inline =
false;
155 if (!can_inline)
return NoChange();
158 if (node->opcode() == IrOpcode::kJSCall) {
159 CallParameters
const p = CallParametersOf(node->op());
160 candidate.frequency = p.frequency();
162 ConstructParameters
const p = ConstructParametersOf(node->op());
163 candidate.frequency = p.frequency();
170 case kRestrictedInlining:
172 case kStressInlining:
173 return InlineCandidate(candidate,
false);
174 case kGeneralInlining:
181 if (candidate.frequency.IsKnown() &&
182 candidate.frequency.value() < FLAG_min_inlining_frequency) {
189 cumulative_count_ < FLAG_max_inlined_bytecode_size_absolute) {
190 TRACE(
"Inlining small function(s) at call site #%d:%s\n", node->id(),
191 node->op()->mnemonic());
192 return InlineCandidate(candidate,
true);
196 candidates_.insert(candidate);
200 void JSInliningHeuristic::Finalize() {
201 if (candidates_.empty())
return;
202 if (FLAG_trace_turbo_inlining) PrintCandidates();
208 while (!candidates_.empty()) {
209 auto i = candidates_.begin();
210 Candidate candidate = *
i;
211 candidates_.erase(
i);
215 double size_of_candidate =
216 candidate.total_size * FLAG_reserve_inline_budget_scale_factor;
217 int total_size = cumulative_count_ +
static_cast<int>(size_of_candidate);
218 if (total_size > FLAG_max_inlined_bytecode_size_cumulative) {
224 if (!candidate.node->IsDead()) {
225 Reduction
const reduction = InlineCandidate(candidate,
false);
226 if (reduction.Changed())
return;
233 struct NodeAndIndex {
238 bool CollectStateValuesOwnedUses(Node* node, Node* state_values,
239 NodeAndIndex* uses_buffer,
size_t* use_count,
242 if (state_values->UseCount() > 1)
return true;
243 for (
int i = 0;
i < state_values->InputCount();
i++) {
244 Node* input = state_values->InputAt(
i);
245 if (input->opcode() == IrOpcode::kStateValues) {
246 if (!CollectStateValuesOwnedUses(node, input, uses_buffer, use_count,
250 }
else if (input == node) {
251 if (*use_count >= max_uses)
return false;
252 uses_buffer[*use_count] = {state_values,
i};
261 Node* JSInliningHeuristic::DuplicateStateValuesAndRename(Node* state_values,
262 Node* from, Node* to,
263 StateCloneMode mode) {
266 if (state_values->UseCount() > 1)
return state_values;
267 Node* copy = mode == kChangeInPlace ? state_values :
nullptr;
268 for (
int i = 0;
i < state_values->InputCount();
i++) {
269 Node* input = state_values->InputAt(
i);
271 if (input->opcode() == IrOpcode::kStateValues) {
272 processed = DuplicateStateValuesAndRename(input, from, to, mode);
273 }
else if (input == from) {
278 if (processed != input) {
280 copy = graph()->CloneNode(state_values);
282 copy->ReplaceInput(
i, processed);
285 return copy ? copy : state_values;
290 bool CollectFrameStateUniqueUses(Node* node, Node* frame_state,
291 NodeAndIndex* uses_buffer,
size_t* use_count,
294 if (frame_state->UseCount() > 1)
return true;
295 if (frame_state->InputAt(kFrameStateStackInput) == node) {
296 if (*use_count >= max_uses)
return false;
297 uses_buffer[*use_count] = {frame_state, kFrameStateStackInput};
300 if (!CollectStateValuesOwnedUses(node,
301 frame_state->InputAt(kFrameStateLocalsInput),
302 uses_buffer, use_count, max_uses)) {
310 Node* JSInliningHeuristic::DuplicateFrameStateAndRename(Node* frame_state,
311 Node* from, Node* to,
312 StateCloneMode mode) {
315 if (frame_state->UseCount() > 1)
return frame_state;
316 Node* copy = mode == kChangeInPlace ? frame_state :
nullptr;
317 if (frame_state->InputAt(kFrameStateStackInput) == from) {
319 copy = graph()->CloneNode(frame_state);
321 copy->ReplaceInput(kFrameStateStackInput, to);
323 Node* locals = frame_state->InputAt(kFrameStateLocalsInput);
324 Node* new_locals = DuplicateStateValuesAndRename(locals, from, to, mode);
325 if (new_locals != locals) {
327 copy = graph()->CloneNode(frame_state);
329 copy->ReplaceInput(kFrameStateLocalsInput, new_locals);
331 return copy ? copy : frame_state;
334 bool JSInliningHeuristic::TryReuseDispatch(Node* node, Node* callee,
335 Candidate
const& candidate,
336 Node** if_successes, Node** calls,
337 Node** inputs,
int input_count) {
343 int const num_calls = candidate.num_functions;
345 DCHECK_EQ(IrOpcode::kPhi, callee->opcode());
346 DCHECK_EQ(num_calls, callee->op()->ValueInputCount());
432 Node* merge = NodeProperties::GetControlInput(callee);
433 if (NodeProperties::GetControlInput(node) != merge)
return false;
439 Node* checkpoint =
nullptr;
440 Node* effect = NodeProperties::GetEffectInput(node);
441 if (effect->opcode() == IrOpcode::kCheckpoint) {
443 if (NodeProperties::GetControlInput(checkpoint) != merge)
return false;
444 effect = NodeProperties::GetEffectInput(effect);
446 if (effect->opcode() != IrOpcode::kEffectPhi)
return false;
447 if (NodeProperties::GetControlInput(effect) != merge)
return false;
448 Node* effect_phi = effect;
452 for (Node* merge_use : merge->uses()) {
453 if (merge_use != effect_phi && merge_use != callee && merge_use != node &&
454 merge_use != checkpoint) {
460 for (Node* effect_phi_use : effect_phi->uses()) {
461 if (effect_phi_use != node && effect_phi_use != checkpoint)
return false;
486 const size_t kMaxUses = 8;
487 NodeAndIndex replaceable_uses[kMaxUses];
488 size_t replaceable_uses_count = 0;
491 Node* checkpoint_state =
nullptr;
493 checkpoint_state = checkpoint->InputAt(0);
494 if (!CollectFrameStateUniqueUses(callee, checkpoint_state, replaceable_uses,
495 &replaceable_uses_count, kMaxUses)) {
501 Node* frame_state = NodeProperties::GetFrameStateInput(node);
502 if (!CollectFrameStateUniqueUses(callee, frame_state, replaceable_uses,
503 &replaceable_uses_count, kMaxUses)) {
509 for (Edge edge : callee->use_edges()) {
511 if (edge.from() == node && edge.index() == 0)
continue;
514 for (
size_t i = 0;
i < replaceable_uses_count;
i++) {
515 if (replaceable_uses[
i].node == edge.from() &&
516 replaceable_uses[
i].index == edge.index()) {
521 if (!found)
return false;
526 for (
int i = 0;
i < num_calls; ++
i) {
532 Node* target = callee->InputAt(
i);
533 Node* effect = effect_phi->InputAt(
i);
534 Node* control = merge->InputAt(
i);
538 Node* new_checkpoint_state = DuplicateFrameStateAndRename(
539 checkpoint_state, callee, target,
540 (
i == num_calls - 1) ? kChangeInPlace : kCloneState);
541 effect = graph()->NewNode(checkpoint->op(), new_checkpoint_state, effect,
546 Node* new_lazy_frame_state = DuplicateFrameStateAndRename(
547 frame_state, callee, target,
548 (
i == num_calls - 1) ? kChangeInPlace : kCloneState);
550 inputs[input_count - 3] = new_lazy_frame_state;
551 inputs[input_count - 2] = effect;
552 inputs[input_count - 1] = control;
553 calls[
i] = if_successes[
i] =
554 graph()->NewNode(node->op(), input_count, inputs);
558 node->ReplaceInput(input_count - 1, jsgraph()->Dead());
559 callee->ReplaceInput(num_calls, jsgraph()->Dead());
560 effect_phi->ReplaceInput(num_calls, jsgraph()->Dead());
562 checkpoint->ReplaceInput(2, jsgraph()->Dead());
569 void JSInliningHeuristic::CreateOrReuseDispatch(Node* node, Node* callee,
570 Candidate
const& candidate,
572 Node** calls, Node** inputs,
574 SourcePositionTable::Scope position(
575 source_positions_, source_positions_->GetSourcePosition(node));
576 if (TryReuseDispatch(node, callee, candidate, if_successes, calls, inputs,
581 Node* fallthrough_control = NodeProperties::GetControlInput(node);
582 int const num_calls = candidate.num_functions;
585 for (
int i = 0;
i < num_calls; ++
i) {
588 Node* target = jsgraph()->HeapConstant(candidate.functions[
i]);
589 if (
i != (num_calls - 1)) {
591 graph()->NewNode(simplified()->ReferenceEqual(), callee, target);
593 graph()->NewNode(common()->Branch(), check, fallthrough_control);
594 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
595 if_successes[
i] = graph()->NewNode(common()->IfTrue(), branch);
597 if_successes[
i] = fallthrough_control;
606 if (node->opcode() == IrOpcode::kJSConstruct && inputs[0] == inputs[1]) {
610 inputs[input_count - 1] = if_successes[
i];
611 calls[
i] = if_successes[
i] =
612 graph()->NewNode(node->op(), input_count, inputs);
616 Reduction JSInliningHeuristic::InlineCandidate(Candidate
const& candidate,
617 bool small_function) {
618 int const num_calls = candidate.num_functions;
619 Node*
const node = candidate.node;
620 if (num_calls == 1) {
621 Reduction
const reduction = inliner_.ReduceJSCall(node);
622 if (reduction.Changed()) {
623 cumulative_count_ += candidate.bytecode[0]->length();
630 DCHECK_LT(1, num_calls);
631 Node* calls[kMaxCallPolymorphism + 1];
632 Node* if_successes[kMaxCallPolymorphism];
633 Node* callee = NodeProperties::GetValueInput(node, 0);
636 int const input_count = node->InputCount();
637 Node** inputs = graph()->zone()->NewArray<Node*>(input_count);
638 for (
int i = 0;
i < input_count; ++
i) {
639 inputs[
i] = node->InputAt(
i);
643 CreateOrReuseDispatch(node, callee, candidate, if_successes, calls, inputs,
647 Node* if_exception =
nullptr;
648 if (NodeProperties::IsExceptionalCall(node, &if_exception)) {
649 Node* if_exceptions[kMaxCallPolymorphism + 1];
650 for (
int i = 0;
i < num_calls; ++
i) {
651 if_successes[
i] = graph()->NewNode(common()->IfSuccess(), calls[
i]);
653 graph()->NewNode(common()->IfException(), calls[
i], calls[
i]);
657 Node* exception_control =
658 graph()->NewNode(common()->Merge(num_calls), num_calls, if_exceptions);
659 if_exceptions[num_calls] = exception_control;
660 Node* exception_effect = graph()->NewNode(common()->EffectPhi(num_calls),
661 num_calls + 1, if_exceptions);
662 Node* exception_value = graph()->NewNode(
663 common()->Phi(MachineRepresentation::kTagged, num_calls), num_calls + 1,
665 ReplaceWithValue(if_exception, exception_value, exception_effect,
671 graph()->NewNode(common()->Merge(num_calls), num_calls, if_successes);
672 calls[num_calls] = control;
674 graph()->NewNode(common()->EffectPhi(num_calls), num_calls + 1, calls);
676 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, num_calls),
677 num_calls + 1, calls);
678 ReplaceWithValue(node, value, effect, control);
681 for (
int i = 0;
i < num_calls; ++
i) {
682 Node* node = calls[
i];
683 if (small_function ||
684 (candidate.can_inline_function[
i] &&
685 cumulative_count_ < FLAG_max_inlined_bytecode_size_cumulative)) {
686 Reduction
const reduction = inliner_.ReduceJSCall(node);
687 if (reduction.Changed()) {
691 cumulative_count_ += candidate.bytecode[
i]->length();
696 return Replace(value);
699 bool JSInliningHeuristic::CandidateCompare::operator()(
700 const Candidate& left,
const Candidate& right)
const {
701 if (right.frequency.IsUnknown()) {
702 if (left.frequency.IsUnknown()) {
706 return left.node->id() > right.node->id();
709 }
else if (left.frequency.IsUnknown()) {
711 }
else if (left.frequency.value() > right.frequency.value()) {
713 }
else if (left.frequency.value() < right.frequency.value()) {
716 return left.node->id() > right.node->id();
720 void JSInliningHeuristic::PrintCandidates() {
722 os <<
"Candidates for inlining (size=" << candidates_.size() <<
"):\n";
723 for (
const Candidate& candidate : candidates_) {
724 os <<
" #" << candidate.node->id() <<
":" 725 << candidate.node->op()->mnemonic()
726 <<
", frequency: " << candidate.frequency << std::endl;
727 for (
int i = 0;
i < candidate.num_functions; ++
i) {
728 Handle<SharedFunctionInfo> shared =
729 candidate.functions[
i].is_null()
730 ? candidate.shared_info
731 : handle(candidate.functions[
i]->shared(), isolate());
732 PrintF(
" - size:%d, name: %s\n", candidate.bytecode[
i]->length(),
733 shared->DebugName()->ToCString().get());
738 Graph* JSInliningHeuristic::graph()
const {
return jsgraph()->graph(); }
740 CommonOperatorBuilder* JSInliningHeuristic::common()
const {
741 return jsgraph()->common();
744 SimplifiedOperatorBuilder* JSInliningHeuristic::simplified()
const {
745 return jsgraph()->simplified();