11cb0ef41Sopenharmony_ci// Copyright 2015 the V8 project authors. All rights reserved. 21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be 31cb0ef41Sopenharmony_ci// found in the LICENSE file. 41cb0ef41Sopenharmony_ci 51cb0ef41Sopenharmony_ci#include "src/compiler/js-inlining-heuristic.h" 61cb0ef41Sopenharmony_ci 71cb0ef41Sopenharmony_ci#include "src/codegen/optimized-compilation-info.h" 81cb0ef41Sopenharmony_ci#include "src/compiler/common-operator.h" 91cb0ef41Sopenharmony_ci#include "src/compiler/compiler-source-position-table.h" 101cb0ef41Sopenharmony_ci#include "src/compiler/js-heap-broker.h" 111cb0ef41Sopenharmony_ci#include "src/compiler/node-matchers.h" 121cb0ef41Sopenharmony_ci#include "src/compiler/simplified-operator.h" 131cb0ef41Sopenharmony_ci#include "src/objects/objects-inl.h" 141cb0ef41Sopenharmony_ci 151cb0ef41Sopenharmony_cinamespace v8 { 161cb0ef41Sopenharmony_cinamespace internal { 171cb0ef41Sopenharmony_cinamespace compiler { 181cb0ef41Sopenharmony_ci 191cb0ef41Sopenharmony_ci#define TRACE(...) \ 201cb0ef41Sopenharmony_ci do { \ 211cb0ef41Sopenharmony_ci if (FLAG_trace_turbo_inlining) StdoutStream{} << __VA_ARGS__ << std::endl; \ 221cb0ef41Sopenharmony_ci } while (false) 231cb0ef41Sopenharmony_ci 241cb0ef41Sopenharmony_cinamespace { 251cb0ef41Sopenharmony_cibool IsSmall(int const size) { 261cb0ef41Sopenharmony_ci return size <= FLAG_max_inlined_bytecode_size_small; 271cb0ef41Sopenharmony_ci} 281cb0ef41Sopenharmony_ci 291cb0ef41Sopenharmony_cibool CanConsiderForInlining(JSHeapBroker* broker, 301cb0ef41Sopenharmony_ci FeedbackCellRef const& feedback_cell) { 311cb0ef41Sopenharmony_ci base::Optional<FeedbackVectorRef> feedback_vector = 321cb0ef41Sopenharmony_ci feedback_cell.feedback_vector(); 331cb0ef41Sopenharmony_ci if (!feedback_vector.has_value()) { 341cb0ef41Sopenharmony_ci TRACE("Cannot consider " << feedback_cell 351cb0ef41Sopenharmony_ci << " for inlining (no feedback vector)"); 361cb0ef41Sopenharmony_ci return false; 371cb0ef41Sopenharmony_ci } 381cb0ef41Sopenharmony_ci SharedFunctionInfoRef shared = feedback_vector->shared_function_info(); 391cb0ef41Sopenharmony_ci 401cb0ef41Sopenharmony_ci if (!shared.HasBytecodeArray()) { 411cb0ef41Sopenharmony_ci TRACE("Cannot consider " << shared << " for inlining (no bytecode)"); 421cb0ef41Sopenharmony_ci return false; 431cb0ef41Sopenharmony_ci } 441cb0ef41Sopenharmony_ci // Ensure we have a persistent handle to the bytecode in order to avoid 451cb0ef41Sopenharmony_ci // flushing it during the remaining compilation. 461cb0ef41Sopenharmony_ci shared.GetBytecodeArray(); 471cb0ef41Sopenharmony_ci 481cb0ef41Sopenharmony_ci // Read feedback vector again in case it got flushed before we were able to 491cb0ef41Sopenharmony_ci // prevent flushing above. 501cb0ef41Sopenharmony_ci base::Optional<FeedbackVectorRef> feedback_vector_again = 511cb0ef41Sopenharmony_ci feedback_cell.feedback_vector(); 521cb0ef41Sopenharmony_ci if (!feedback_vector_again.has_value()) { 531cb0ef41Sopenharmony_ci TRACE("Cannot consider " << shared << " for inlining (no feedback vector)"); 541cb0ef41Sopenharmony_ci return false; 551cb0ef41Sopenharmony_ci } 561cb0ef41Sopenharmony_ci if (!feedback_vector_again->equals(*feedback_vector)) { 571cb0ef41Sopenharmony_ci // The new feedback vector likely contains lots of uninitialized slots, so 581cb0ef41Sopenharmony_ci // it doesn't make much sense to inline this function now. 591cb0ef41Sopenharmony_ci TRACE("Not considering " << shared 601cb0ef41Sopenharmony_ci << " for inlining (feedback vector changed)"); 611cb0ef41Sopenharmony_ci return false; 621cb0ef41Sopenharmony_ci } 631cb0ef41Sopenharmony_ci 641cb0ef41Sopenharmony_ci SharedFunctionInfo::Inlineability inlineability = shared.GetInlineability(); 651cb0ef41Sopenharmony_ci if (inlineability != SharedFunctionInfo::kIsInlineable) { 661cb0ef41Sopenharmony_ci TRACE("Cannot consider " 671cb0ef41Sopenharmony_ci << shared << " for inlining (reason: " << inlineability << ")"); 681cb0ef41Sopenharmony_ci return false; 691cb0ef41Sopenharmony_ci } 701cb0ef41Sopenharmony_ci 711cb0ef41Sopenharmony_ci TRACE("Considering " << shared << " for inlining with " << *feedback_vector); 721cb0ef41Sopenharmony_ci return true; 731cb0ef41Sopenharmony_ci} 741cb0ef41Sopenharmony_ci 751cb0ef41Sopenharmony_cibool CanConsiderForInlining(JSHeapBroker* broker, 761cb0ef41Sopenharmony_ci JSFunctionRef const& function) { 771cb0ef41Sopenharmony_ci FeedbackCellRef feedback_cell = 781cb0ef41Sopenharmony_ci function.raw_feedback_cell(broker->dependencies()); 791cb0ef41Sopenharmony_ci bool const result = CanConsiderForInlining(broker, feedback_cell); 801cb0ef41Sopenharmony_ci if (result) { 811cb0ef41Sopenharmony_ci CHECK( 821cb0ef41Sopenharmony_ci function.shared().equals(feedback_cell.shared_function_info().value())); 831cb0ef41Sopenharmony_ci } 841cb0ef41Sopenharmony_ci return result; 851cb0ef41Sopenharmony_ci} 861cb0ef41Sopenharmony_ci 871cb0ef41Sopenharmony_ci} // namespace 881cb0ef41Sopenharmony_ci 891cb0ef41Sopenharmony_ciJSInliningHeuristic::Candidate JSInliningHeuristic::CollectFunctions( 901cb0ef41Sopenharmony_ci Node* node, int functions_size) { 911cb0ef41Sopenharmony_ci DCHECK_NE(0, functions_size); 921cb0ef41Sopenharmony_ci Node* callee = node->InputAt(0); 931cb0ef41Sopenharmony_ci Candidate out; 941cb0ef41Sopenharmony_ci out.node = node; 951cb0ef41Sopenharmony_ci 961cb0ef41Sopenharmony_ci HeapObjectMatcher m(callee); 971cb0ef41Sopenharmony_ci if (m.HasResolvedValue() && m.Ref(broker()).IsJSFunction()) { 981cb0ef41Sopenharmony_ci JSFunctionRef function = m.Ref(broker()).AsJSFunction(); 991cb0ef41Sopenharmony_ci out.functions[0] = function; 1001cb0ef41Sopenharmony_ci if (CanConsiderForInlining(broker(), function)) { 1011cb0ef41Sopenharmony_ci out.bytecode[0] = function.shared().GetBytecodeArray(); 1021cb0ef41Sopenharmony_ci out.num_functions = 1; 1031cb0ef41Sopenharmony_ci return out; 1041cb0ef41Sopenharmony_ci } 1051cb0ef41Sopenharmony_ci } 1061cb0ef41Sopenharmony_ci if (m.IsPhi()) { 1071cb0ef41Sopenharmony_ci int const value_input_count = m.node()->op()->ValueInputCount(); 1081cb0ef41Sopenharmony_ci if (value_input_count > functions_size) { 1091cb0ef41Sopenharmony_ci out.num_functions = 0; 1101cb0ef41Sopenharmony_ci return out; 1111cb0ef41Sopenharmony_ci } 1121cb0ef41Sopenharmony_ci for (int n = 0; n < value_input_count; ++n) { 1131cb0ef41Sopenharmony_ci HeapObjectMatcher m2(callee->InputAt(n)); 1141cb0ef41Sopenharmony_ci if (!m2.HasResolvedValue() || !m2.Ref(broker()).IsJSFunction()) { 1151cb0ef41Sopenharmony_ci out.num_functions = 0; 1161cb0ef41Sopenharmony_ci return out; 1171cb0ef41Sopenharmony_ci } 1181cb0ef41Sopenharmony_ci 1191cb0ef41Sopenharmony_ci out.functions[n] = m2.Ref(broker()).AsJSFunction(); 1201cb0ef41Sopenharmony_ci JSFunctionRef function = out.functions[n].value(); 1211cb0ef41Sopenharmony_ci if (CanConsiderForInlining(broker(), function)) { 1221cb0ef41Sopenharmony_ci out.bytecode[n] = function.shared().GetBytecodeArray(); 1231cb0ef41Sopenharmony_ci } 1241cb0ef41Sopenharmony_ci } 1251cb0ef41Sopenharmony_ci out.num_functions = value_input_count; 1261cb0ef41Sopenharmony_ci return out; 1271cb0ef41Sopenharmony_ci } 1281cb0ef41Sopenharmony_ci if (m.IsCheckClosure()) { 1291cb0ef41Sopenharmony_ci DCHECK(!out.functions[0].has_value()); 1301cb0ef41Sopenharmony_ci FeedbackCellRef feedback_cell = MakeRef(broker(), FeedbackCellOf(m.op())); 1311cb0ef41Sopenharmony_ci if (CanConsiderForInlining(broker(), feedback_cell)) { 1321cb0ef41Sopenharmony_ci out.shared_info = feedback_cell.shared_function_info().value(); 1331cb0ef41Sopenharmony_ci out.bytecode[0] = out.shared_info->GetBytecodeArray(); 1341cb0ef41Sopenharmony_ci } 1351cb0ef41Sopenharmony_ci out.num_functions = 1; 1361cb0ef41Sopenharmony_ci return out; 1371cb0ef41Sopenharmony_ci } 1381cb0ef41Sopenharmony_ci if (m.IsJSCreateClosure()) { 1391cb0ef41Sopenharmony_ci DCHECK(!out.functions[0].has_value()); 1401cb0ef41Sopenharmony_ci JSCreateClosureNode n(callee); 1411cb0ef41Sopenharmony_ci FeedbackCellRef feedback_cell = n.GetFeedbackCellRefChecked(broker()); 1421cb0ef41Sopenharmony_ci if (CanConsiderForInlining(broker(), feedback_cell)) { 1431cb0ef41Sopenharmony_ci out.shared_info = feedback_cell.shared_function_info().value(); 1441cb0ef41Sopenharmony_ci out.bytecode[0] = out.shared_info->GetBytecodeArray(); 1451cb0ef41Sopenharmony_ci CHECK(out.shared_info->equals(n.Parameters().shared_info(broker()))); 1461cb0ef41Sopenharmony_ci } 1471cb0ef41Sopenharmony_ci out.num_functions = 1; 1481cb0ef41Sopenharmony_ci return out; 1491cb0ef41Sopenharmony_ci } 1501cb0ef41Sopenharmony_ci out.num_functions = 0; 1511cb0ef41Sopenharmony_ci return out; 1521cb0ef41Sopenharmony_ci} 1531cb0ef41Sopenharmony_ci 1541cb0ef41Sopenharmony_ciReduction JSInliningHeuristic::Reduce(Node* node) { 1551cb0ef41Sopenharmony_ci#if V8_ENABLE_WEBASSEMBLY 1561cb0ef41Sopenharmony_ci if (mode() == kWasmOnly) { 1571cb0ef41Sopenharmony_ci if (node->opcode() == IrOpcode::kJSWasmCall) { 1581cb0ef41Sopenharmony_ci return inliner_.ReduceJSWasmCall(node); 1591cb0ef41Sopenharmony_ci } 1601cb0ef41Sopenharmony_ci return NoChange(); 1611cb0ef41Sopenharmony_ci } 1621cb0ef41Sopenharmony_ci#endif // V8_ENABLE_WEBASSEMBLY 1631cb0ef41Sopenharmony_ci 1641cb0ef41Sopenharmony_ci DCHECK_EQ(mode(), kJSOnly); 1651cb0ef41Sopenharmony_ci if (!IrOpcode::IsInlineeOpcode(node->opcode())) return NoChange(); 1661cb0ef41Sopenharmony_ci 1671cb0ef41Sopenharmony_ci if (total_inlined_bytecode_size_ >= max_inlined_bytecode_size_absolute_) { 1681cb0ef41Sopenharmony_ci return NoChange(); 1691cb0ef41Sopenharmony_ci } 1701cb0ef41Sopenharmony_ci 1711cb0ef41Sopenharmony_ci // Check if we already saw that {node} before, and if so, just skip it. 1721cb0ef41Sopenharmony_ci if (seen_.find(node->id()) != seen_.end()) return NoChange(); 1731cb0ef41Sopenharmony_ci 1741cb0ef41Sopenharmony_ci // Check if the {node} is an appropriate candidate for inlining. 1751cb0ef41Sopenharmony_ci Candidate candidate = CollectFunctions(node, kMaxCallPolymorphism); 1761cb0ef41Sopenharmony_ci if (candidate.num_functions == 0) { 1771cb0ef41Sopenharmony_ci return NoChange(); 1781cb0ef41Sopenharmony_ci } else if (candidate.num_functions > 1 && !FLAG_polymorphic_inlining) { 1791cb0ef41Sopenharmony_ci TRACE("Not considering call site #" 1801cb0ef41Sopenharmony_ci << node->id() << ":" << node->op()->mnemonic() 1811cb0ef41Sopenharmony_ci << ", because polymorphic inlining is disabled"); 1821cb0ef41Sopenharmony_ci return NoChange(); 1831cb0ef41Sopenharmony_ci } 1841cb0ef41Sopenharmony_ci 1851cb0ef41Sopenharmony_ci bool can_inline_candidate = false, candidate_is_small = true; 1861cb0ef41Sopenharmony_ci candidate.total_size = 0; 1871cb0ef41Sopenharmony_ci FrameState frame_state{NodeProperties::GetFrameStateInput(node)}; 1881cb0ef41Sopenharmony_ci FrameStateInfo const& frame_info = frame_state.frame_state_info(); 1891cb0ef41Sopenharmony_ci Handle<SharedFunctionInfo> frame_shared_info; 1901cb0ef41Sopenharmony_ci for (int i = 0; i < candidate.num_functions; ++i) { 1911cb0ef41Sopenharmony_ci if (!candidate.bytecode[i].has_value()) { 1921cb0ef41Sopenharmony_ci candidate.can_inline_function[i] = false; 1931cb0ef41Sopenharmony_ci continue; 1941cb0ef41Sopenharmony_ci } 1951cb0ef41Sopenharmony_ci 1961cb0ef41Sopenharmony_ci SharedFunctionInfoRef shared = candidate.functions[i].has_value() 1971cb0ef41Sopenharmony_ci ? candidate.functions[i].value().shared() 1981cb0ef41Sopenharmony_ci : candidate.shared_info.value(); 1991cb0ef41Sopenharmony_ci candidate.can_inline_function[i] = candidate.bytecode[i].has_value(); 2001cb0ef41Sopenharmony_ci // Because of concurrent optimization, optimization of the inlining 2011cb0ef41Sopenharmony_ci // candidate could have been disabled meanwhile. 2021cb0ef41Sopenharmony_ci // JSInliner will check this again and not actually inline the function in 2031cb0ef41Sopenharmony_ci // this case. 2041cb0ef41Sopenharmony_ci CHECK_IMPLIES(candidate.can_inline_function[i], 2051cb0ef41Sopenharmony_ci shared.IsInlineable() || 2061cb0ef41Sopenharmony_ci shared.GetInlineability() == 2071cb0ef41Sopenharmony_ci SharedFunctionInfo::kHasOptimizationDisabled); 2081cb0ef41Sopenharmony_ci // Do not allow direct recursion i.e. f() -> f(). We still allow indirect 2091cb0ef41Sopenharmony_ci // recursion like f() -> g() -> f(). The indirect recursion is helpful in 2101cb0ef41Sopenharmony_ci // cases where f() is a small dispatch function that calls the appropriate 2111cb0ef41Sopenharmony_ci // function. In the case of direct recursion, we only have some static 2121cb0ef41Sopenharmony_ci // information for the first level of inlining and it may not be that useful 2131cb0ef41Sopenharmony_ci // to just inline one level in recursive calls. In some cases like tail 2141cb0ef41Sopenharmony_ci // recursion we may benefit from recursive inlining, if we have additional 2151cb0ef41Sopenharmony_ci // analysis that converts them to iterative implementations. Though it is 2161cb0ef41Sopenharmony_ci // not obvious if such an analysis is needed. 2171cb0ef41Sopenharmony_ci if (frame_info.shared_info().ToHandle(&frame_shared_info) && 2181cb0ef41Sopenharmony_ci frame_shared_info.equals(shared.object())) { 2191cb0ef41Sopenharmony_ci TRACE("Not considering call site #" << node->id() << ":" 2201cb0ef41Sopenharmony_ci << node->op()->mnemonic() 2211cb0ef41Sopenharmony_ci << ", because of recursive inlining"); 2221cb0ef41Sopenharmony_ci candidate.can_inline_function[i] = false; 2231cb0ef41Sopenharmony_ci } 2241cb0ef41Sopenharmony_ci if (candidate.can_inline_function[i]) { 2251cb0ef41Sopenharmony_ci can_inline_candidate = true; 2261cb0ef41Sopenharmony_ci BytecodeArrayRef bytecode = candidate.bytecode[i].value(); 2271cb0ef41Sopenharmony_ci candidate.total_size += bytecode.length(); 2281cb0ef41Sopenharmony_ci unsigned inlined_bytecode_size = 0; 2291cb0ef41Sopenharmony_ci if (candidate.functions[i].has_value()) { 2301cb0ef41Sopenharmony_ci JSFunctionRef function = candidate.functions[i].value(); 2311cb0ef41Sopenharmony_ci inlined_bytecode_size = function.code().GetInlinedBytecodeSize(); 2321cb0ef41Sopenharmony_ci candidate.total_size += inlined_bytecode_size; 2331cb0ef41Sopenharmony_ci } 2341cb0ef41Sopenharmony_ci candidate_is_small = candidate_is_small && 2351cb0ef41Sopenharmony_ci IsSmall(bytecode.length() + inlined_bytecode_size); 2361cb0ef41Sopenharmony_ci } 2371cb0ef41Sopenharmony_ci } 2381cb0ef41Sopenharmony_ci if (!can_inline_candidate) return NoChange(); 2391cb0ef41Sopenharmony_ci 2401cb0ef41Sopenharmony_ci // Gather feedback on how often this call site has been hit before. 2411cb0ef41Sopenharmony_ci if (node->opcode() == IrOpcode::kJSCall) { 2421cb0ef41Sopenharmony_ci CallParameters const p = CallParametersOf(node->op()); 2431cb0ef41Sopenharmony_ci candidate.frequency = p.frequency(); 2441cb0ef41Sopenharmony_ci } else { 2451cb0ef41Sopenharmony_ci ConstructParameters const p = ConstructParametersOf(node->op()); 2461cb0ef41Sopenharmony_ci candidate.frequency = p.frequency(); 2471cb0ef41Sopenharmony_ci } 2481cb0ef41Sopenharmony_ci 2491cb0ef41Sopenharmony_ci // Don't consider a {candidate} whose frequency is below the 2501cb0ef41Sopenharmony_ci // threshold, i.e. a call site that is only hit once every N 2511cb0ef41Sopenharmony_ci // invocations of the caller. 2521cb0ef41Sopenharmony_ci if (candidate.frequency.IsKnown() && 2531cb0ef41Sopenharmony_ci candidate.frequency.value() < FLAG_min_inlining_frequency) { 2541cb0ef41Sopenharmony_ci return NoChange(); 2551cb0ef41Sopenharmony_ci } 2561cb0ef41Sopenharmony_ci 2571cb0ef41Sopenharmony_ci // Found a candidate. Insert it into the set of seen nodes s.t. we don't 2581cb0ef41Sopenharmony_ci // revisit in the future. Note this insertion happens here and not earlier in 2591cb0ef41Sopenharmony_ci // order to make inlining decisions order-independent. A node may not be a 2601cb0ef41Sopenharmony_ci // candidate when first seen, but later reductions may turn it into a valid 2611cb0ef41Sopenharmony_ci // candidate. In that case, the node should be revisited by 2621cb0ef41Sopenharmony_ci // JSInliningHeuristic. 2631cb0ef41Sopenharmony_ci seen_.insert(node->id()); 2641cb0ef41Sopenharmony_ci 2651cb0ef41Sopenharmony_ci // Forcibly inline small functions here. In the case of polymorphic inlining 2661cb0ef41Sopenharmony_ci // candidate_is_small is set only when all functions are small. 2671cb0ef41Sopenharmony_ci if (candidate_is_small) { 2681cb0ef41Sopenharmony_ci TRACE("Inlining small function(s) at call site #" 2691cb0ef41Sopenharmony_ci << node->id() << ":" << node->op()->mnemonic()); 2701cb0ef41Sopenharmony_ci return InlineCandidate(candidate, true); 2711cb0ef41Sopenharmony_ci } 2721cb0ef41Sopenharmony_ci 2731cb0ef41Sopenharmony_ci // In the general case we remember the candidate for later. 2741cb0ef41Sopenharmony_ci candidates_.insert(candidate); 2751cb0ef41Sopenharmony_ci return NoChange(); 2761cb0ef41Sopenharmony_ci} 2771cb0ef41Sopenharmony_ci 2781cb0ef41Sopenharmony_civoid JSInliningHeuristic::Finalize() { 2791cb0ef41Sopenharmony_ci if (candidates_.empty()) return; // Nothing to do without candidates. 2801cb0ef41Sopenharmony_ci if (FLAG_trace_turbo_inlining) PrintCandidates(); 2811cb0ef41Sopenharmony_ci 2821cb0ef41Sopenharmony_ci // We inline at most one candidate in every iteration of the fixpoint. 2831cb0ef41Sopenharmony_ci // This is to ensure that we don't consume the full inlining budget 2841cb0ef41Sopenharmony_ci // on things that aren't called very often. 2851cb0ef41Sopenharmony_ci // TODO(bmeurer): Use std::priority_queue instead of std::set here. 2861cb0ef41Sopenharmony_ci while (!candidates_.empty()) { 2871cb0ef41Sopenharmony_ci auto i = candidates_.begin(); 2881cb0ef41Sopenharmony_ci Candidate candidate = *i; 2891cb0ef41Sopenharmony_ci candidates_.erase(i); 2901cb0ef41Sopenharmony_ci 2911cb0ef41Sopenharmony_ci // Ignore this candidate if it's no longer valid. 2921cb0ef41Sopenharmony_ci if (!IrOpcode::IsInlineeOpcode(candidate.node->opcode())) continue; 2931cb0ef41Sopenharmony_ci if (candidate.node->IsDead()) continue; 2941cb0ef41Sopenharmony_ci 2951cb0ef41Sopenharmony_ci // Make sure we have some extra budget left, so that any small functions 2961cb0ef41Sopenharmony_ci // exposed by this function would be given a chance to inline. 2971cb0ef41Sopenharmony_ci double size_of_candidate = 2981cb0ef41Sopenharmony_ci candidate.total_size * FLAG_reserve_inline_budget_scale_factor; 2991cb0ef41Sopenharmony_ci int total_size = 3001cb0ef41Sopenharmony_ci total_inlined_bytecode_size_ + static_cast<int>(size_of_candidate); 3011cb0ef41Sopenharmony_ci if (total_size > max_inlined_bytecode_size_cumulative_) { 3021cb0ef41Sopenharmony_ci // Try if any smaller functions are available to inline. 3031cb0ef41Sopenharmony_ci continue; 3041cb0ef41Sopenharmony_ci } 3051cb0ef41Sopenharmony_ci 3061cb0ef41Sopenharmony_ci Reduction const reduction = InlineCandidate(candidate, false); 3071cb0ef41Sopenharmony_ci if (reduction.Changed()) return; 3081cb0ef41Sopenharmony_ci } 3091cb0ef41Sopenharmony_ci} 3101cb0ef41Sopenharmony_ci 3111cb0ef41Sopenharmony_cinamespace { 3121cb0ef41Sopenharmony_ci 3131cb0ef41Sopenharmony_cistruct NodeAndIndex { 3141cb0ef41Sopenharmony_ci Node* node; 3151cb0ef41Sopenharmony_ci int index; 3161cb0ef41Sopenharmony_ci}; 3171cb0ef41Sopenharmony_ci 3181cb0ef41Sopenharmony_cibool CollectStateValuesOwnedUses(Node* node, Node* state_values, 3191cb0ef41Sopenharmony_ci NodeAndIndex* uses_buffer, size_t* use_count, 3201cb0ef41Sopenharmony_ci size_t max_uses) { 3211cb0ef41Sopenharmony_ci // Only accumulate states that are not shared with other users. 3221cb0ef41Sopenharmony_ci if (state_values->UseCount() > 1) return true; 3231cb0ef41Sopenharmony_ci for (int i = 0; i < state_values->InputCount(); i++) { 3241cb0ef41Sopenharmony_ci Node* input = state_values->InputAt(i); 3251cb0ef41Sopenharmony_ci if (input->opcode() == IrOpcode::kStateValues) { 3261cb0ef41Sopenharmony_ci if (!CollectStateValuesOwnedUses(node, input, uses_buffer, use_count, 3271cb0ef41Sopenharmony_ci max_uses)) { 3281cb0ef41Sopenharmony_ci return false; 3291cb0ef41Sopenharmony_ci } 3301cb0ef41Sopenharmony_ci } else if (input == node) { 3311cb0ef41Sopenharmony_ci if (*use_count >= max_uses) return false; 3321cb0ef41Sopenharmony_ci uses_buffer[*use_count] = {state_values, i}; 3331cb0ef41Sopenharmony_ci (*use_count)++; 3341cb0ef41Sopenharmony_ci } 3351cb0ef41Sopenharmony_ci } 3361cb0ef41Sopenharmony_ci return true; 3371cb0ef41Sopenharmony_ci} 3381cb0ef41Sopenharmony_ci 3391cb0ef41Sopenharmony_ci} // namespace 3401cb0ef41Sopenharmony_ci 3411cb0ef41Sopenharmony_ciNode* JSInliningHeuristic::DuplicateStateValuesAndRename(Node* state_values, 3421cb0ef41Sopenharmony_ci Node* from, Node* to, 3431cb0ef41Sopenharmony_ci StateCloneMode mode) { 3441cb0ef41Sopenharmony_ci // Only rename in states that are not shared with other users. This needs to 3451cb0ef41Sopenharmony_ci // be in sync with the condition in {CollectStateValuesOwnedUses}. 3461cb0ef41Sopenharmony_ci if (state_values->UseCount() > 1) return state_values; 3471cb0ef41Sopenharmony_ci Node* copy = mode == kChangeInPlace ? state_values : nullptr; 3481cb0ef41Sopenharmony_ci for (int i = 0; i < state_values->InputCount(); i++) { 3491cb0ef41Sopenharmony_ci Node* input = state_values->InputAt(i); 3501cb0ef41Sopenharmony_ci Node* processed; 3511cb0ef41Sopenharmony_ci if (input->opcode() == IrOpcode::kStateValues) { 3521cb0ef41Sopenharmony_ci processed = DuplicateStateValuesAndRename(input, from, to, mode); 3531cb0ef41Sopenharmony_ci } else if (input == from) { 3541cb0ef41Sopenharmony_ci processed = to; 3551cb0ef41Sopenharmony_ci } else { 3561cb0ef41Sopenharmony_ci processed = input; 3571cb0ef41Sopenharmony_ci } 3581cb0ef41Sopenharmony_ci if (processed != input) { 3591cb0ef41Sopenharmony_ci if (!copy) { 3601cb0ef41Sopenharmony_ci copy = graph()->CloneNode(state_values); 3611cb0ef41Sopenharmony_ci } 3621cb0ef41Sopenharmony_ci copy->ReplaceInput(i, processed); 3631cb0ef41Sopenharmony_ci } 3641cb0ef41Sopenharmony_ci } 3651cb0ef41Sopenharmony_ci return copy ? copy : state_values; 3661cb0ef41Sopenharmony_ci} 3671cb0ef41Sopenharmony_ci 3681cb0ef41Sopenharmony_cinamespace { 3691cb0ef41Sopenharmony_ci 3701cb0ef41Sopenharmony_cibool CollectFrameStateUniqueUses(Node* node, FrameState frame_state, 3711cb0ef41Sopenharmony_ci NodeAndIndex* uses_buffer, size_t* use_count, 3721cb0ef41Sopenharmony_ci size_t max_uses) { 3731cb0ef41Sopenharmony_ci // Only accumulate states that are not shared with other users. 3741cb0ef41Sopenharmony_ci if (frame_state->UseCount() > 1) return true; 3751cb0ef41Sopenharmony_ci if (frame_state.stack() == node) { 3761cb0ef41Sopenharmony_ci if (*use_count >= max_uses) return false; 3771cb0ef41Sopenharmony_ci uses_buffer[*use_count] = {frame_state, FrameState::kFrameStateStackInput}; 3781cb0ef41Sopenharmony_ci (*use_count)++; 3791cb0ef41Sopenharmony_ci } 3801cb0ef41Sopenharmony_ci if (!CollectStateValuesOwnedUses(node, frame_state.locals(), uses_buffer, 3811cb0ef41Sopenharmony_ci use_count, max_uses)) { 3821cb0ef41Sopenharmony_ci return false; 3831cb0ef41Sopenharmony_ci } 3841cb0ef41Sopenharmony_ci return true; 3851cb0ef41Sopenharmony_ci} 3861cb0ef41Sopenharmony_ci 3871cb0ef41Sopenharmony_ci} // namespace 3881cb0ef41Sopenharmony_ci 3891cb0ef41Sopenharmony_ciFrameState JSInliningHeuristic::DuplicateFrameStateAndRename( 3901cb0ef41Sopenharmony_ci FrameState frame_state, Node* from, Node* to, StateCloneMode mode) { 3911cb0ef41Sopenharmony_ci // Only rename in states that are not shared with other users. This needs to 3921cb0ef41Sopenharmony_ci // be in sync with the condition in {DuplicateFrameStateAndRename}. 3931cb0ef41Sopenharmony_ci if (frame_state->UseCount() > 1) return frame_state; 3941cb0ef41Sopenharmony_ci Node* copy = 3951cb0ef41Sopenharmony_ci mode == kChangeInPlace ? static_cast<Node*>(frame_state) : nullptr; 3961cb0ef41Sopenharmony_ci if (frame_state.stack() == from) { 3971cb0ef41Sopenharmony_ci if (!copy) { 3981cb0ef41Sopenharmony_ci copy = graph()->CloneNode(frame_state); 3991cb0ef41Sopenharmony_ci } 4001cb0ef41Sopenharmony_ci copy->ReplaceInput(FrameState::kFrameStateStackInput, to); 4011cb0ef41Sopenharmony_ci } 4021cb0ef41Sopenharmony_ci Node* locals = frame_state.locals(); 4031cb0ef41Sopenharmony_ci Node* new_locals = DuplicateStateValuesAndRename(locals, from, to, mode); 4041cb0ef41Sopenharmony_ci if (new_locals != locals) { 4051cb0ef41Sopenharmony_ci if (!copy) { 4061cb0ef41Sopenharmony_ci copy = graph()->CloneNode(frame_state); 4071cb0ef41Sopenharmony_ci } 4081cb0ef41Sopenharmony_ci copy->ReplaceInput(FrameState::kFrameStateLocalsInput, new_locals); 4091cb0ef41Sopenharmony_ci } 4101cb0ef41Sopenharmony_ci return copy != nullptr ? FrameState{copy} : frame_state; 4111cb0ef41Sopenharmony_ci} 4121cb0ef41Sopenharmony_ci 4131cb0ef41Sopenharmony_cibool JSInliningHeuristic::TryReuseDispatch(Node* node, Node* callee, 4141cb0ef41Sopenharmony_ci Node** if_successes, Node** calls, 4151cb0ef41Sopenharmony_ci Node** inputs, int input_count) { 4161cb0ef41Sopenharmony_ci // We will try to reuse the control flow branch created for computing 4171cb0ef41Sopenharmony_ci // the {callee} target of the call. We only reuse the branch if there 4181cb0ef41Sopenharmony_ci // is no side-effect between the call and the branch, and if the callee is 4191cb0ef41Sopenharmony_ci // only used as the target (and possibly also in the related frame states). 4201cb0ef41Sopenharmony_ci 4211cb0ef41Sopenharmony_ci // We are trying to match the following pattern: 4221cb0ef41Sopenharmony_ci // 4231cb0ef41Sopenharmony_ci // C1 C2 4241cb0ef41Sopenharmony_ci // . . 4251cb0ef41Sopenharmony_ci // | | 4261cb0ef41Sopenharmony_ci // Merge(merge) <-----------------+ 4271cb0ef41Sopenharmony_ci // ^ ^ | 4281cb0ef41Sopenharmony_ci // V1 V2 | | E1 E2 | 4291cb0ef41Sopenharmony_ci // . . | +----+ . . | 4301cb0ef41Sopenharmony_ci // | | | | | | | 4311cb0ef41Sopenharmony_ci // Phi(callee) EffectPhi(effect_phi) | 4321cb0ef41Sopenharmony_ci // ^ ^ | 4331cb0ef41Sopenharmony_ci // | | | 4341cb0ef41Sopenharmony_ci // +----+ | | 4351cb0ef41Sopenharmony_ci // | | | | 4361cb0ef41Sopenharmony_ci // | StateValues | | 4371cb0ef41Sopenharmony_ci // | ^ | | 4381cb0ef41Sopenharmony_ci // +----+ | | | 4391cb0ef41Sopenharmony_ci // | | | | | 4401cb0ef41Sopenharmony_ci // | FrameState | | 4411cb0ef41Sopenharmony_ci // | ^ | | 4421cb0ef41Sopenharmony_ci // | | | +---+ 4431cb0ef41Sopenharmony_ci // | | | | | 4441cb0ef41Sopenharmony_ci // +----+ Checkpoint(checkpoint) | 4451cb0ef41Sopenharmony_ci // | | ^ | 4461cb0ef41Sopenharmony_ci // | StateValues | +-------------+ 4471cb0ef41Sopenharmony_ci // | | | | 4481cb0ef41Sopenharmony_ci // +-----+ | | | 4491cb0ef41Sopenharmony_ci // | | | | | 4501cb0ef41Sopenharmony_ci // | FrameState | | 4511cb0ef41Sopenharmony_ci // | ^ | | 4521cb0ef41Sopenharmony_ci // +-----------+ | | | 4531cb0ef41Sopenharmony_ci // Call(node) 4541cb0ef41Sopenharmony_ci // | 4551cb0ef41Sopenharmony_ci // | 4561cb0ef41Sopenharmony_ci // . 4571cb0ef41Sopenharmony_ci // 4581cb0ef41Sopenharmony_ci // The {callee} here is a phi that merges the possible call targets, {node} 4591cb0ef41Sopenharmony_ci // is the actual call that we will try to duplicate and connect to the 4601cb0ef41Sopenharmony_ci // control that comes into {merge}. There can be a {checkpoint} between 4611cb0ef41Sopenharmony_ci // the call and the calle phi. 4621cb0ef41Sopenharmony_ci // 4631cb0ef41Sopenharmony_ci // The idea is to get rid of the merge, effect phi and phi, then duplicate 4641cb0ef41Sopenharmony_ci // the call (with all the frame states and such), and connect the duplicated 4651cb0ef41Sopenharmony_ci // calls and states directly to the inputs of the ex-phi, ex-effect-phi and 4661cb0ef41Sopenharmony_ci // ex-merge. The tricky part is to make sure that there is no interference 4671cb0ef41Sopenharmony_ci // from the outside. In particular, there should not be any unaccounted uses 4681cb0ef41Sopenharmony_ci // of the phi, effect-phi and merge because we will remove them from 4691cb0ef41Sopenharmony_ci // the graph. 4701cb0ef41Sopenharmony_ci // 4711cb0ef41Sopenharmony_ci // V1 E1 C1 V2 E2 C2 4721cb0ef41Sopenharmony_ci // . . . . . . 4731cb0ef41Sopenharmony_ci // | | | | | | 4741cb0ef41Sopenharmony_ci // +----+ | | +----+ | 4751cb0ef41Sopenharmony_ci // | | | | | | | 4761cb0ef41Sopenharmony_ci // | StateValues | | | StateValues | 4771cb0ef41Sopenharmony_ci // | ^ | | | ^ | 4781cb0ef41Sopenharmony_ci // +----+ | | | +----+ | | 4791cb0ef41Sopenharmony_ci // | | | | | | | | | 4801cb0ef41Sopenharmony_ci // | FrameState | | | FrameState | 4811cb0ef41Sopenharmony_ci // | ^ | | | ^ | 4821cb0ef41Sopenharmony_ci // | | | | | | | 4831cb0ef41Sopenharmony_ci // | | | | | | | 4841cb0ef41Sopenharmony_ci // +----+ Checkpoint | +----+ Checkpoint | 4851cb0ef41Sopenharmony_ci // | | ^ | | | ^ | 4861cb0ef41Sopenharmony_ci // | StateValues | | | StateValues | | 4871cb0ef41Sopenharmony_ci // | | | | | | | | 4881cb0ef41Sopenharmony_ci // +-----+ | | | +-----+ | | | 4891cb0ef41Sopenharmony_ci // | | | | | | | | | | 4901cb0ef41Sopenharmony_ci // | FrameState | | | FrameState | | 4911cb0ef41Sopenharmony_ci // | ^ | | | ^ | | 4921cb0ef41Sopenharmony_ci // +-------------+| | | +-------------+| | | 4931cb0ef41Sopenharmony_ci // Call----+ Call----+ 4941cb0ef41Sopenharmony_ci // | | 4951cb0ef41Sopenharmony_ci // +-------+ +------------+ 4961cb0ef41Sopenharmony_ci // | | 4971cb0ef41Sopenharmony_ci // Merge 4981cb0ef41Sopenharmony_ci // EffectPhi 4991cb0ef41Sopenharmony_ci // Phi 5001cb0ef41Sopenharmony_ci // | 5011cb0ef41Sopenharmony_ci // ... 5021cb0ef41Sopenharmony_ci 5031cb0ef41Sopenharmony_ci // Bailout if the call is not polymorphic anymore (other reducers might 5041cb0ef41Sopenharmony_ci // have replaced the callee phi with a constant). 5051cb0ef41Sopenharmony_ci if (callee->opcode() != IrOpcode::kPhi) return false; 5061cb0ef41Sopenharmony_ci int const num_calls = callee->op()->ValueInputCount(); 5071cb0ef41Sopenharmony_ci 5081cb0ef41Sopenharmony_ci // If there is a control node between the callee computation 5091cb0ef41Sopenharmony_ci // and the call, bail out. 5101cb0ef41Sopenharmony_ci Node* merge = NodeProperties::GetControlInput(callee); 5111cb0ef41Sopenharmony_ci if (NodeProperties::GetControlInput(node) != merge) return false; 5121cb0ef41Sopenharmony_ci 5131cb0ef41Sopenharmony_ci // If there is a non-checkpoint effect node between the callee computation 5141cb0ef41Sopenharmony_ci // and the call, bail out. We will drop any checkpoint between the call and 5151cb0ef41Sopenharmony_ci // the callee phi because the callee computation should have its own 5161cb0ef41Sopenharmony_ci // checkpoint that the call can fall back to. 5171cb0ef41Sopenharmony_ci Node* checkpoint = nullptr; 5181cb0ef41Sopenharmony_ci Node* effect = NodeProperties::GetEffectInput(node); 5191cb0ef41Sopenharmony_ci if (effect->opcode() == IrOpcode::kCheckpoint) { 5201cb0ef41Sopenharmony_ci checkpoint = effect; 5211cb0ef41Sopenharmony_ci if (NodeProperties::GetControlInput(checkpoint) != merge) return false; 5221cb0ef41Sopenharmony_ci effect = NodeProperties::GetEffectInput(effect); 5231cb0ef41Sopenharmony_ci } 5241cb0ef41Sopenharmony_ci if (effect->opcode() != IrOpcode::kEffectPhi) return false; 5251cb0ef41Sopenharmony_ci if (NodeProperties::GetControlInput(effect) != merge) return false; 5261cb0ef41Sopenharmony_ci Node* effect_phi = effect; 5271cb0ef41Sopenharmony_ci 5281cb0ef41Sopenharmony_ci // The effect phi, the callee, the call and the checkpoint must be the only 5291cb0ef41Sopenharmony_ci // users of the merge. 5301cb0ef41Sopenharmony_ci for (Node* merge_use : merge->uses()) { 5311cb0ef41Sopenharmony_ci if (merge_use != effect_phi && merge_use != callee && merge_use != node && 5321cb0ef41Sopenharmony_ci merge_use != checkpoint) { 5331cb0ef41Sopenharmony_ci return false; 5341cb0ef41Sopenharmony_ci } 5351cb0ef41Sopenharmony_ci } 5361cb0ef41Sopenharmony_ci 5371cb0ef41Sopenharmony_ci // The effect phi must be only used by the checkpoint or the call. 5381cb0ef41Sopenharmony_ci for (Node* effect_phi_use : effect_phi->uses()) { 5391cb0ef41Sopenharmony_ci if (effect_phi_use != node && effect_phi_use != checkpoint) return false; 5401cb0ef41Sopenharmony_ci } 5411cb0ef41Sopenharmony_ci 5421cb0ef41Sopenharmony_ci // We must replace the callee phi with the appropriate constant in 5431cb0ef41Sopenharmony_ci // the entire subgraph reachable by inputs from the call (terminating 5441cb0ef41Sopenharmony_ci // at phis and merges). Since we do not want to walk (and later duplicate) 5451cb0ef41Sopenharmony_ci // the subgraph here, we limit the possible uses to this set: 5461cb0ef41Sopenharmony_ci // 5471cb0ef41Sopenharmony_ci // 1. In the call (as a target). 5481cb0ef41Sopenharmony_ci // 2. The checkpoint between the call and the callee computation merge. 5491cb0ef41Sopenharmony_ci // 3. The lazy deoptimization frame state. 5501cb0ef41Sopenharmony_ci // 5511cb0ef41Sopenharmony_ci // This corresponds to the most common pattern, where the function is 5521cb0ef41Sopenharmony_ci // called with only local variables or constants as arguments. 5531cb0ef41Sopenharmony_ci // 5541cb0ef41Sopenharmony_ci // To check the uses, we first collect all the occurrences of callee in 1, 2 5551cb0ef41Sopenharmony_ci // and 3, and then we check that all uses of callee are in the collected 5561cb0ef41Sopenharmony_ci // occurrences. If there is an unaccounted use, we do not try to rewire 5571cb0ef41Sopenharmony_ci // the control flow. 5581cb0ef41Sopenharmony_ci // 5591cb0ef41Sopenharmony_ci // Note: With CFG, this would be much easier and more robust - we would just 5601cb0ef41Sopenharmony_ci // duplicate all the nodes between the merge and the call, replacing all 5611cb0ef41Sopenharmony_ci // occurrences of the {callee} phi with the appropriate constant. 5621cb0ef41Sopenharmony_ci 5631cb0ef41Sopenharmony_ci // First compute the set of uses that are only reachable from 2 and 3. 5641cb0ef41Sopenharmony_ci const size_t kMaxUses = 8; 5651cb0ef41Sopenharmony_ci NodeAndIndex replaceable_uses[kMaxUses]; 5661cb0ef41Sopenharmony_ci size_t replaceable_uses_count = 0; 5671cb0ef41Sopenharmony_ci 5681cb0ef41Sopenharmony_ci // Collect the uses to check case 2. 5691cb0ef41Sopenharmony_ci Node* checkpoint_state = nullptr; 5701cb0ef41Sopenharmony_ci if (checkpoint) { 5711cb0ef41Sopenharmony_ci checkpoint_state = checkpoint->InputAt(0); 5721cb0ef41Sopenharmony_ci if (!CollectFrameStateUniqueUses(callee, FrameState{checkpoint_state}, 5731cb0ef41Sopenharmony_ci replaceable_uses, &replaceable_uses_count, 5741cb0ef41Sopenharmony_ci kMaxUses)) { 5751cb0ef41Sopenharmony_ci return false; 5761cb0ef41Sopenharmony_ci } 5771cb0ef41Sopenharmony_ci } 5781cb0ef41Sopenharmony_ci 5791cb0ef41Sopenharmony_ci // Collect the uses to check case 3. 5801cb0ef41Sopenharmony_ci FrameState frame_state{NodeProperties::GetFrameStateInput(node)}; 5811cb0ef41Sopenharmony_ci if (!CollectFrameStateUniqueUses(callee, frame_state, replaceable_uses, 5821cb0ef41Sopenharmony_ci &replaceable_uses_count, kMaxUses)) { 5831cb0ef41Sopenharmony_ci return false; 5841cb0ef41Sopenharmony_ci } 5851cb0ef41Sopenharmony_ci 5861cb0ef41Sopenharmony_ci // Bail out if there is a use of {callee} that is not reachable from 1, 2 5871cb0ef41Sopenharmony_ci // and 3. 5881cb0ef41Sopenharmony_ci for (Edge edge : callee->use_edges()) { 5891cb0ef41Sopenharmony_ci // Case 1 (use by the call as a target). 5901cb0ef41Sopenharmony_ci if (edge.from() == node && edge.index() == 0) continue; 5911cb0ef41Sopenharmony_ci // Case 2 and 3 - used in checkpoint and/or lazy deopt frame states. 5921cb0ef41Sopenharmony_ci bool found = false; 5931cb0ef41Sopenharmony_ci for (size_t i = 0; i < replaceable_uses_count; i++) { 5941cb0ef41Sopenharmony_ci if (replaceable_uses[i].node == edge.from() && 5951cb0ef41Sopenharmony_ci replaceable_uses[i].index == edge.index()) { 5961cb0ef41Sopenharmony_ci found = true; 5971cb0ef41Sopenharmony_ci break; 5981cb0ef41Sopenharmony_ci } 5991cb0ef41Sopenharmony_ci } 6001cb0ef41Sopenharmony_ci if (!found) return false; 6011cb0ef41Sopenharmony_ci } 6021cb0ef41Sopenharmony_ci 6031cb0ef41Sopenharmony_ci // Clone the call and the framestate, including the uniquely reachable 6041cb0ef41Sopenharmony_ci // state values, making sure that we replace the phi with the constant. 6051cb0ef41Sopenharmony_ci for (int i = 0; i < num_calls; ++i) { 6061cb0ef41Sopenharmony_ci // Clone the calls for each branch. 6071cb0ef41Sopenharmony_ci // We need to specialize the calls to the correct target, effect, and 6081cb0ef41Sopenharmony_ci // control. We also need to duplicate the checkpoint and the lazy 6091cb0ef41Sopenharmony_ci // frame state, and change all the uses of the callee to the constant 6101cb0ef41Sopenharmony_ci // callee. 6111cb0ef41Sopenharmony_ci Node* target = callee->InputAt(i); 6121cb0ef41Sopenharmony_ci Node* effect_phi_effect = effect_phi->InputAt(i); 6131cb0ef41Sopenharmony_ci Node* control = merge->InputAt(i); 6141cb0ef41Sopenharmony_ci 6151cb0ef41Sopenharmony_ci if (checkpoint) { 6161cb0ef41Sopenharmony_ci // Duplicate the checkpoint. 6171cb0ef41Sopenharmony_ci FrameState new_checkpoint_state = DuplicateFrameStateAndRename( 6181cb0ef41Sopenharmony_ci FrameState{checkpoint_state}, callee, target, 6191cb0ef41Sopenharmony_ci (i == num_calls - 1) ? kChangeInPlace : kCloneState); 6201cb0ef41Sopenharmony_ci effect_phi_effect = graph()->NewNode( 6211cb0ef41Sopenharmony_ci checkpoint->op(), new_checkpoint_state, effect_phi_effect, control); 6221cb0ef41Sopenharmony_ci } 6231cb0ef41Sopenharmony_ci 6241cb0ef41Sopenharmony_ci // Duplicate the call. 6251cb0ef41Sopenharmony_ci FrameState new_lazy_frame_state = DuplicateFrameStateAndRename( 6261cb0ef41Sopenharmony_ci frame_state, callee, target, 6271cb0ef41Sopenharmony_ci (i == num_calls - 1) ? kChangeInPlace : kCloneState); 6281cb0ef41Sopenharmony_ci inputs[0] = target; 6291cb0ef41Sopenharmony_ci inputs[input_count - 3] = new_lazy_frame_state; 6301cb0ef41Sopenharmony_ci inputs[input_count - 2] = effect_phi_effect; 6311cb0ef41Sopenharmony_ci inputs[input_count - 1] = control; 6321cb0ef41Sopenharmony_ci calls[i] = if_successes[i] = 6331cb0ef41Sopenharmony_ci graph()->NewNode(node->op(), input_count, inputs); 6341cb0ef41Sopenharmony_ci } 6351cb0ef41Sopenharmony_ci 6361cb0ef41Sopenharmony_ci // Mark the control inputs dead, so that we can kill the merge. 6371cb0ef41Sopenharmony_ci node->ReplaceInput(input_count - 1, jsgraph()->Dead()); 6381cb0ef41Sopenharmony_ci callee->ReplaceInput(num_calls, jsgraph()->Dead()); 6391cb0ef41Sopenharmony_ci effect_phi->ReplaceInput(num_calls, jsgraph()->Dead()); 6401cb0ef41Sopenharmony_ci if (checkpoint) { 6411cb0ef41Sopenharmony_ci checkpoint->ReplaceInput(2, jsgraph()->Dead()); 6421cb0ef41Sopenharmony_ci } 6431cb0ef41Sopenharmony_ci 6441cb0ef41Sopenharmony_ci merge->Kill(); 6451cb0ef41Sopenharmony_ci return true; 6461cb0ef41Sopenharmony_ci} 6471cb0ef41Sopenharmony_ci 6481cb0ef41Sopenharmony_civoid JSInliningHeuristic::CreateOrReuseDispatch(Node* node, Node* callee, 6491cb0ef41Sopenharmony_ci Candidate const& candidate, 6501cb0ef41Sopenharmony_ci Node** if_successes, 6511cb0ef41Sopenharmony_ci Node** calls, Node** inputs, 6521cb0ef41Sopenharmony_ci int input_count) { 6531cb0ef41Sopenharmony_ci SourcePositionTable::Scope position( 6541cb0ef41Sopenharmony_ci source_positions_, source_positions_->GetSourcePosition(node)); 6551cb0ef41Sopenharmony_ci if (TryReuseDispatch(node, callee, if_successes, calls, inputs, 6561cb0ef41Sopenharmony_ci input_count)) { 6571cb0ef41Sopenharmony_ci return; 6581cb0ef41Sopenharmony_ci } 6591cb0ef41Sopenharmony_ci 6601cb0ef41Sopenharmony_ci STATIC_ASSERT(JSCallOrConstructNode::kHaveIdenticalLayouts); 6611cb0ef41Sopenharmony_ci 6621cb0ef41Sopenharmony_ci Node* fallthrough_control = NodeProperties::GetControlInput(node); 6631cb0ef41Sopenharmony_ci int const num_calls = candidate.num_functions; 6641cb0ef41Sopenharmony_ci 6651cb0ef41Sopenharmony_ci // Create the appropriate control flow to dispatch to the cloned calls. 6661cb0ef41Sopenharmony_ci for (int i = 0; i < num_calls; ++i) { 6671cb0ef41Sopenharmony_ci // TODO(2206): Make comparison be based on underlying SharedFunctionInfo 6681cb0ef41Sopenharmony_ci // instead of the target JSFunction reference directly. 6691cb0ef41Sopenharmony_ci Node* target = jsgraph()->Constant(candidate.functions[i].value()); 6701cb0ef41Sopenharmony_ci if (i != (num_calls - 1)) { 6711cb0ef41Sopenharmony_ci Node* check = 6721cb0ef41Sopenharmony_ci graph()->NewNode(simplified()->ReferenceEqual(), callee, target); 6731cb0ef41Sopenharmony_ci Node* branch = 6741cb0ef41Sopenharmony_ci graph()->NewNode(common()->Branch(), check, fallthrough_control); 6751cb0ef41Sopenharmony_ci fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); 6761cb0ef41Sopenharmony_ci if_successes[i] = graph()->NewNode(common()->IfTrue(), branch); 6771cb0ef41Sopenharmony_ci } else { 6781cb0ef41Sopenharmony_ci if_successes[i] = fallthrough_control; 6791cb0ef41Sopenharmony_ci } 6801cb0ef41Sopenharmony_ci 6811cb0ef41Sopenharmony_ci // Clone the calls for each branch. 6821cb0ef41Sopenharmony_ci // The first input to the call is the actual target (which we specialize 6831cb0ef41Sopenharmony_ci // to the known {target}); the last input is the control dependency. 6841cb0ef41Sopenharmony_ci // We also specialize the new.target of JSConstruct {node}s if it refers 6851cb0ef41Sopenharmony_ci // to the same node as the {node}'s target input, so that we can later 6861cb0ef41Sopenharmony_ci // properly inline the JSCreate operations. 6871cb0ef41Sopenharmony_ci if (node->opcode() == IrOpcode::kJSConstruct) { 6881cb0ef41Sopenharmony_ci // TODO(jgruber, v8:10675): This branch seems unreachable. 6891cb0ef41Sopenharmony_ci JSConstructNode n(node); 6901cb0ef41Sopenharmony_ci if (inputs[n.TargetIndex()] == inputs[n.NewTargetIndex()]) { 6911cb0ef41Sopenharmony_ci inputs[n.NewTargetIndex()] = target; 6921cb0ef41Sopenharmony_ci } 6931cb0ef41Sopenharmony_ci } 6941cb0ef41Sopenharmony_ci inputs[JSCallOrConstructNode::TargetIndex()] = target; 6951cb0ef41Sopenharmony_ci inputs[input_count - 1] = if_successes[i]; 6961cb0ef41Sopenharmony_ci calls[i] = if_successes[i] = 6971cb0ef41Sopenharmony_ci graph()->NewNode(node->op(), input_count, inputs); 6981cb0ef41Sopenharmony_ci } 6991cb0ef41Sopenharmony_ci} 7001cb0ef41Sopenharmony_ci 7011cb0ef41Sopenharmony_ciReduction JSInliningHeuristic::InlineCandidate(Candidate const& candidate, 7021cb0ef41Sopenharmony_ci bool small_function) { 7031cb0ef41Sopenharmony_ci int const num_calls = candidate.num_functions; 7041cb0ef41Sopenharmony_ci Node* const node = candidate.node; 7051cb0ef41Sopenharmony_ci#if V8_ENABLE_WEBASSEMBLY 7061cb0ef41Sopenharmony_ci DCHECK_NE(node->opcode(), IrOpcode::kJSWasmCall); 7071cb0ef41Sopenharmony_ci#endif // V8_ENABLE_WEBASSEMBLY 7081cb0ef41Sopenharmony_ci if (num_calls == 1) { 7091cb0ef41Sopenharmony_ci Reduction const reduction = inliner_.ReduceJSCall(node); 7101cb0ef41Sopenharmony_ci if (reduction.Changed()) { 7111cb0ef41Sopenharmony_ci total_inlined_bytecode_size_ += candidate.bytecode[0].value().length(); 7121cb0ef41Sopenharmony_ci } 7131cb0ef41Sopenharmony_ci return reduction; 7141cb0ef41Sopenharmony_ci } 7151cb0ef41Sopenharmony_ci 7161cb0ef41Sopenharmony_ci // Expand the JSCall/JSConstruct node to a subgraph first if 7171cb0ef41Sopenharmony_ci // we have multiple known target functions. 7181cb0ef41Sopenharmony_ci DCHECK_LT(1, num_calls); 7191cb0ef41Sopenharmony_ci Node* calls[kMaxCallPolymorphism + 1]; 7201cb0ef41Sopenharmony_ci Node* if_successes[kMaxCallPolymorphism]; 7211cb0ef41Sopenharmony_ci Node* callee = NodeProperties::GetValueInput(node, 0); 7221cb0ef41Sopenharmony_ci 7231cb0ef41Sopenharmony_ci // Setup the inputs for the cloned call nodes. 7241cb0ef41Sopenharmony_ci int const input_count = node->InputCount(); 7251cb0ef41Sopenharmony_ci Node** inputs = graph()->zone()->NewArray<Node*>(input_count); 7261cb0ef41Sopenharmony_ci for (int i = 0; i < input_count; ++i) { 7271cb0ef41Sopenharmony_ci inputs[i] = node->InputAt(i); 7281cb0ef41Sopenharmony_ci } 7291cb0ef41Sopenharmony_ci 7301cb0ef41Sopenharmony_ci // Create the appropriate control flow to dispatch to the cloned calls. 7311cb0ef41Sopenharmony_ci CreateOrReuseDispatch(node, callee, candidate, if_successes, calls, inputs, 7321cb0ef41Sopenharmony_ci input_count); 7331cb0ef41Sopenharmony_ci 7341cb0ef41Sopenharmony_ci // Check if we have an exception projection for the call {node}. 7351cb0ef41Sopenharmony_ci Node* if_exception = nullptr; 7361cb0ef41Sopenharmony_ci if (NodeProperties::IsExceptionalCall(node, &if_exception)) { 7371cb0ef41Sopenharmony_ci Node* if_exceptions[kMaxCallPolymorphism + 1]; 7381cb0ef41Sopenharmony_ci for (int i = 0; i < num_calls; ++i) { 7391cb0ef41Sopenharmony_ci if_successes[i] = graph()->NewNode(common()->IfSuccess(), calls[i]); 7401cb0ef41Sopenharmony_ci if_exceptions[i] = 7411cb0ef41Sopenharmony_ci graph()->NewNode(common()->IfException(), calls[i], calls[i]); 7421cb0ef41Sopenharmony_ci } 7431cb0ef41Sopenharmony_ci 7441cb0ef41Sopenharmony_ci // Morph the {if_exception} projection into a join. 7451cb0ef41Sopenharmony_ci Node* exception_control = 7461cb0ef41Sopenharmony_ci graph()->NewNode(common()->Merge(num_calls), num_calls, if_exceptions); 7471cb0ef41Sopenharmony_ci if_exceptions[num_calls] = exception_control; 7481cb0ef41Sopenharmony_ci Node* exception_effect = graph()->NewNode(common()->EffectPhi(num_calls), 7491cb0ef41Sopenharmony_ci num_calls + 1, if_exceptions); 7501cb0ef41Sopenharmony_ci Node* exception_value = graph()->NewNode( 7511cb0ef41Sopenharmony_ci common()->Phi(MachineRepresentation::kTagged, num_calls), num_calls + 1, 7521cb0ef41Sopenharmony_ci if_exceptions); 7531cb0ef41Sopenharmony_ci ReplaceWithValue(if_exception, exception_value, exception_effect, 7541cb0ef41Sopenharmony_ci exception_control); 7551cb0ef41Sopenharmony_ci } 7561cb0ef41Sopenharmony_ci 7571cb0ef41Sopenharmony_ci // Morph the original call site into a join of the dispatched call sites. 7581cb0ef41Sopenharmony_ci Node* control = 7591cb0ef41Sopenharmony_ci graph()->NewNode(common()->Merge(num_calls), num_calls, if_successes); 7601cb0ef41Sopenharmony_ci calls[num_calls] = control; 7611cb0ef41Sopenharmony_ci Node* effect = 7621cb0ef41Sopenharmony_ci graph()->NewNode(common()->EffectPhi(num_calls), num_calls + 1, calls); 7631cb0ef41Sopenharmony_ci Node* value = 7641cb0ef41Sopenharmony_ci graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, num_calls), 7651cb0ef41Sopenharmony_ci num_calls + 1, calls); 7661cb0ef41Sopenharmony_ci ReplaceWithValue(node, value, effect, control); 7671cb0ef41Sopenharmony_ci 7681cb0ef41Sopenharmony_ci // Inline the individual, cloned call sites. 7691cb0ef41Sopenharmony_ci for (int i = 0; i < num_calls && total_inlined_bytecode_size_ < 7701cb0ef41Sopenharmony_ci max_inlined_bytecode_size_absolute_; 7711cb0ef41Sopenharmony_ci ++i) { 7721cb0ef41Sopenharmony_ci if (candidate.can_inline_function[i] && 7731cb0ef41Sopenharmony_ci (small_function || total_inlined_bytecode_size_ < 7741cb0ef41Sopenharmony_ci max_inlined_bytecode_size_cumulative_)) { 7751cb0ef41Sopenharmony_ci Node* call = calls[i]; 7761cb0ef41Sopenharmony_ci Reduction const reduction = inliner_.ReduceJSCall(call); 7771cb0ef41Sopenharmony_ci if (reduction.Changed()) { 7781cb0ef41Sopenharmony_ci total_inlined_bytecode_size_ += candidate.bytecode[i]->length(); 7791cb0ef41Sopenharmony_ci // Killing the call node is not strictly necessary, but it is safer to 7801cb0ef41Sopenharmony_ci // make sure we do not resurrect the node. 7811cb0ef41Sopenharmony_ci call->Kill(); 7821cb0ef41Sopenharmony_ci } 7831cb0ef41Sopenharmony_ci } 7841cb0ef41Sopenharmony_ci } 7851cb0ef41Sopenharmony_ci 7861cb0ef41Sopenharmony_ci return Replace(value); 7871cb0ef41Sopenharmony_ci} 7881cb0ef41Sopenharmony_ci 7891cb0ef41Sopenharmony_cibool JSInliningHeuristic::CandidateCompare::operator()( 7901cb0ef41Sopenharmony_ci const Candidate& left, const Candidate& right) const { 7911cb0ef41Sopenharmony_ci if (right.frequency.IsUnknown()) { 7921cb0ef41Sopenharmony_ci if (left.frequency.IsUnknown()) { 7931cb0ef41Sopenharmony_ci // If left and right are both unknown then the ordering is indeterminate, 7941cb0ef41Sopenharmony_ci // which breaks strict weak ordering requirements, so we fall back to the 7951cb0ef41Sopenharmony_ci // node id as a tie breaker. 7961cb0ef41Sopenharmony_ci return left.node->id() > right.node->id(); 7971cb0ef41Sopenharmony_ci } 7981cb0ef41Sopenharmony_ci return true; 7991cb0ef41Sopenharmony_ci } else if (left.frequency.IsUnknown()) { 8001cb0ef41Sopenharmony_ci return false; 8011cb0ef41Sopenharmony_ci } else if (left.frequency.value() > right.frequency.value()) { 8021cb0ef41Sopenharmony_ci return true; 8031cb0ef41Sopenharmony_ci } else if (left.frequency.value() < right.frequency.value()) { 8041cb0ef41Sopenharmony_ci return false; 8051cb0ef41Sopenharmony_ci } else { 8061cb0ef41Sopenharmony_ci return left.node->id() > right.node->id(); 8071cb0ef41Sopenharmony_ci } 8081cb0ef41Sopenharmony_ci} 8091cb0ef41Sopenharmony_ci 8101cb0ef41Sopenharmony_civoid JSInliningHeuristic::PrintCandidates() { 8111cb0ef41Sopenharmony_ci StdoutStream os; 8121cb0ef41Sopenharmony_ci os << candidates_.size() << " candidate(s) for inlining:" << std::endl; 8131cb0ef41Sopenharmony_ci for (const Candidate& candidate : candidates_) { 8141cb0ef41Sopenharmony_ci os << "- candidate: " << candidate.node->op()->mnemonic() << " node #" 8151cb0ef41Sopenharmony_ci << candidate.node->id() << " with frequency " << candidate.frequency 8161cb0ef41Sopenharmony_ci << ", " << candidate.num_functions << " target(s):" << std::endl; 8171cb0ef41Sopenharmony_ci for (int i = 0; i < candidate.num_functions; ++i) { 8181cb0ef41Sopenharmony_ci SharedFunctionInfoRef shared = candidate.functions[i].has_value() 8191cb0ef41Sopenharmony_ci ? candidate.functions[i]->shared() 8201cb0ef41Sopenharmony_ci : candidate.shared_info.value(); 8211cb0ef41Sopenharmony_ci os << " - target: " << shared; 8221cb0ef41Sopenharmony_ci if (candidate.bytecode[i].has_value()) { 8231cb0ef41Sopenharmony_ci os << ", bytecode size: " << candidate.bytecode[i]->length(); 8241cb0ef41Sopenharmony_ci if (candidate.functions[i].has_value()) { 8251cb0ef41Sopenharmony_ci JSFunctionRef function = candidate.functions[i].value(); 8261cb0ef41Sopenharmony_ci unsigned inlined_bytecode_size = 8271cb0ef41Sopenharmony_ci function.code().GetInlinedBytecodeSize(); 8281cb0ef41Sopenharmony_ci if (inlined_bytecode_size > 0) { 8291cb0ef41Sopenharmony_ci os << ", existing opt code's inlined bytecode size: " 8301cb0ef41Sopenharmony_ci << inlined_bytecode_size; 8311cb0ef41Sopenharmony_ci } 8321cb0ef41Sopenharmony_ci } 8331cb0ef41Sopenharmony_ci } else { 8341cb0ef41Sopenharmony_ci os << ", no bytecode"; 8351cb0ef41Sopenharmony_ci } 8361cb0ef41Sopenharmony_ci os << std::endl; 8371cb0ef41Sopenharmony_ci } 8381cb0ef41Sopenharmony_ci } 8391cb0ef41Sopenharmony_ci} 8401cb0ef41Sopenharmony_ci 8411cb0ef41Sopenharmony_ciGraph* JSInliningHeuristic::graph() const { return jsgraph()->graph(); } 8421cb0ef41Sopenharmony_ci 8431cb0ef41Sopenharmony_ciCompilationDependencies* JSInliningHeuristic::dependencies() const { 8441cb0ef41Sopenharmony_ci return broker()->dependencies(); 8451cb0ef41Sopenharmony_ci} 8461cb0ef41Sopenharmony_ci 8471cb0ef41Sopenharmony_ciCommonOperatorBuilder* JSInliningHeuristic::common() const { 8481cb0ef41Sopenharmony_ci return jsgraph()->common(); 8491cb0ef41Sopenharmony_ci} 8501cb0ef41Sopenharmony_ci 8511cb0ef41Sopenharmony_ciSimplifiedOperatorBuilder* JSInliningHeuristic::simplified() const { 8521cb0ef41Sopenharmony_ci return jsgraph()->simplified(); 8531cb0ef41Sopenharmony_ci} 8541cb0ef41Sopenharmony_ci 8551cb0ef41Sopenharmony_ci#undef TRACE 8561cb0ef41Sopenharmony_ci 8571cb0ef41Sopenharmony_ci} // namespace compiler 8581cb0ef41Sopenharmony_ci} // namespace internal 8591cb0ef41Sopenharmony_ci} // namespace v8 860