@@ -88,10 +88,21 @@ static cl::opt<bool> InlineEnableCostBenefitAnalysis(
88
88
" inline-enable-cost-benefit-analysis" , cl::Hidden, cl::init(false ),
89
89
cl::desc(" Enable the cost-benefit analysis for the inliner" ));
90
90
91
+ // InlineSavingsMultiplier overrides per TTI multipliers iff it is
92
+ // specified explicitly in command line options. This option is exposed
93
+ // for tuning and testing.
91
94
static cl::opt<int > InlineSavingsMultiplier (
92
95
" inline-savings-multiplier" , cl::Hidden, cl::init(8 ),
93
96
cl::desc(" Multiplier to multiply cycle savings by during inlining" ));
94
97
98
+ // InlineSavingsProfitableMultiplier overrides per TTI multipliers iff it is
99
+ // specified explicitly in command line options. This option is exposed
100
+ // for tuning and testing.
101
+ static cl::opt<int > InlineSavingsProfitableMultiplier (
102
+ " inline-savings-profitable-multiplier" , cl::Hidden, cl::init(4 ),
103
+ cl::desc(" A multiplier on top of cycle savings to decide whether the "
104
+ " savings won't justify the cost" ));
105
+
95
106
static cl::opt<int >
96
107
InlineSizeAllowance (" inline-size-allowance" , cl::Hidden, cl::init(100 ),
97
108
cl::desc(" The maximum size of a callee that get's "
@@ -815,6 +826,32 @@ class InlineCostCallAnalyzer final : public CallAnalyzer {
815
826
return true ;
816
827
}
817
828
829
+ // A helper function to choose between command line override and default.
830
+ unsigned getInliningCostBenefitAnalysisSavingsMultiplier () const {
831
+ if (InlineSavingsMultiplier.getNumOccurrences ())
832
+ return InlineSavingsMultiplier;
833
+ return TTI.getInliningCostBenefitAnalysisSavingsMultiplier ();
834
+ }
835
+
836
+ // A helper function to choose between command line override and default.
837
+ unsigned getInliningCostBenefitAnalysisProfitableMultiplier () const {
838
+ if (InlineSavingsProfitableMultiplier.getNumOccurrences ())
839
+ return InlineSavingsProfitableMultiplier;
840
+ return TTI.getInliningCostBenefitAnalysisProfitableMultiplier ();
841
+ }
842
+
843
+ void OverrideCycleSavingsAndSizeForTesting (APInt &CycleSavings, int &Size) {
844
+ if (std::optional<int > AttrCycleSavings = getStringFnAttrAsInt (
845
+ CandidateCall, " inline-cycle-savings-for-test" )) {
846
+ CycleSavings = *AttrCycleSavings;
847
+ }
848
+
849
+ if (std::optional<int > AttrRuntimeCost = getStringFnAttrAsInt (
850
+ CandidateCall, " inline-runtime-cost-for-test" )) {
851
+ Size = *AttrRuntimeCost;
852
+ }
853
+ }
854
+
818
855
// Determine whether we should inline the given call site, taking into account
819
856
// both the size cost and the cycle savings. Return std::nullopt if we don't
820
857
// have sufficient profiling information to determine.
@@ -884,29 +921,55 @@ class InlineCostCallAnalyzer final : public CallAnalyzer {
884
921
CycleSavings += getCallsiteCost (this ->CandidateCall , DL);
885
922
CycleSavings *= *CallerBFI->getBlockProfileCount (CallerBB);
886
923
887
- // Remove the cost of the cold basic blocks.
924
+ // Remove the cost of the cold basic blocks to model the runtime cost more
925
+ // accurately. Both machine block placement and function splitting could
926
+ // place cold blocks further from hot blocks.
888
927
int Size = Cost - ColdSize;
889
928
890
929
// Allow tiny callees to be inlined regardless of whether they meet the
891
930
// savings threshold.
892
931
Size = Size > InlineSizeAllowance ? Size - InlineSizeAllowance : 1 ;
893
932
933
+ OverrideCycleSavingsAndSizeForTesting (CycleSavings, Size);
894
934
CostBenefit.emplace (APInt (128 , Size), CycleSavings);
895
935
896
- // Return true if the savings justify the cost of inlining. Specifically,
897
- // we evaluate the following inequality:
936
+ // Let R be the ratio of CycleSavings to Size. We accept the inlining
937
+ // opportunity if R is really high and reject if R is really low. If R is
938
+ // somewhere in the middle, we fall back to the cost-based analysis.
898
939
//
899
- // CycleSavings PSI->getOrCompHotCountThreshold()
900
- // -------------- >= -----------------------------------
901
- // Size InlineSavingsMultiplier
940
+ // Specifically, let R = CycleSavings / Size, we accept the inlining
941
+ // opportunity if:
902
942
//
903
- // Note that the left hand side is specific to a call site. The right hand
904
- // side is a constant for the entire executable.
905
- APInt LHS = CycleSavings;
906
- LHS *= InlineSavingsMultiplier;
907
- APInt RHS (128 , PSI->getOrCompHotCountThreshold ());
908
- RHS *= Size;
909
- return LHS.uge (RHS);
943
+ // PSI->getOrCompHotCountThreshold()
944
+ // R > -------------------------------------------------
945
+ // getInliningCostBenefitAnalysisSavingsMultiplier()
946
+ //
947
+ // and reject the inlining opportunity if:
948
+ //
949
+ // PSI->getOrCompHotCountThreshold()
950
+ // R <= ----------------------------------------------------
951
+ // getInliningCostBenefitAnalysisProfitableMultiplier()
952
+ //
953
+ // Otherwise, we fall back to the cost-based analysis.
954
+ //
955
+ // Implementation-wise, use multiplication (CycleSavings * Multiplier,
956
+ // HotCountThreshold * Size) rather than division to avoid precision loss.
957
+ APInt Threshold (128 , PSI->getOrCompHotCountThreshold ());
958
+ Threshold *= Size;
959
+
960
+ APInt UpperBoundCycleSavings = CycleSavings;
961
+ UpperBoundCycleSavings *= getInliningCostBenefitAnalysisSavingsMultiplier ();
962
+ if (UpperBoundCycleSavings.uge (Threshold))
963
+ return true ;
964
+
965
+ APInt LowerBoundCycleSavings = CycleSavings;
966
+ LowerBoundCycleSavings *=
967
+ getInliningCostBenefitAnalysisProfitableMultiplier ();
968
+ if (LowerBoundCycleSavings.ult (Threshold))
969
+ return false ;
970
+
971
+ // Otherwise, fall back to the cost-based analysis.
972
+ return std::nullopt;
910
973
}
911
974
912
975
InlineResult finalizeAnalysis () override {
0 commit comments