Skip to content

Commit 6037a69

Browse files
authored
[mlgo] inline for size: add bypass mechanism for perserving performance (#95616)
This allows shrinking for size the cold part of the code, without sacrificing performance.
1 parent 16aa39a commit 6037a69

File tree

4 files changed

+107
-5
lines changed

4 files changed

+107
-5
lines changed

llvm/include/llvm/Analysis/MLInlineAdvisor.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "llvm/Analysis/InlineAdvisor.h"
1414
#include "llvm/Analysis/LazyCallGraph.h"
1515
#include "llvm/Analysis/MLModelRunner.h"
16+
#include "llvm/Analysis/ProfileSummaryInfo.h"
1617
#include "llvm/IR/PassManager.h"
1718

1819
#include <deque>
@@ -89,6 +90,7 @@ class MLInlineAdvisor : public InlineAdvisor {
8990
llvm::SmallPtrSet<const LazyCallGraph::Node *, 1> NodesInLastSCC;
9091
DenseSet<const LazyCallGraph::Node *> AllNodes;
9192
bool ForceStop = false;
93+
ProfileSummaryInfo &PSI;
9294
};
9395

9496
/// InlineAdvice that tracks changes post inlining. For that reason, it only

llvm/lib/Analysis/MLInlineAdvisor.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "llvm/Analysis/MLInlineAdvisor.h"
1515
#include "llvm/ADT/SCCIterator.h"
1616
#include "llvm/Analysis/AssumptionCache.h"
17+
#include "llvm/Analysis/BlockFrequencyInfo.h"
1718
#include "llvm/Analysis/CallGraph.h"
1819
#include "llvm/Analysis/FunctionPropertiesAnalysis.h"
1920
#include "llvm/Analysis/InlineCost.h"
@@ -23,6 +24,7 @@
2324
#include "llvm/Analysis/LoopInfo.h"
2425
#include "llvm/Analysis/MLModelRunner.h"
2526
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
27+
#include "llvm/Analysis/ProfileSummaryInfo.h"
2628
#include "llvm/Analysis/ReleaseModeModelRunner.h"
2729
#include "llvm/Analysis/TargetTransformInfo.h"
2830
#include "llvm/IR/Dominators.h"
@@ -46,6 +48,14 @@ static cl::opt<bool>
4648
InteractiveIncludeDefault("inliner-interactive-include-default", cl::Hidden,
4749
cl::desc(InclDefaultMsg));
4850

51+
enum class SkipMLPolicyCriteria { Never, IfCallerIsNotCold };
52+
53+
static cl::opt<SkipMLPolicyCriteria> SkipPolicy(
54+
"ml-inliner-skip-policy", cl::Hidden, cl::init(SkipMLPolicyCriteria::Never),
55+
cl::values(clEnumValN(SkipMLPolicyCriteria::Never, "never", "never"),
56+
clEnumValN(SkipMLPolicyCriteria::IfCallerIsNotCold,
57+
"if-caller-not-cold", "if the caller is not cold")));
58+
4959
#if defined(LLVM_HAVE_TF_AOT_INLINERSIZEMODEL)
5060
// codegen-ed file
5161
#include "InlinerSizeModel.h" // NOLINT
@@ -129,7 +139,8 @@ MLInlineAdvisor::MLInlineAdvisor(
129139
M, MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager()),
130140
ModelRunner(std::move(Runner)), GetDefaultAdvice(GetDefaultAdvice),
131141
CG(MAM.getResult<LazyCallGraphAnalysis>(M)),
132-
InitialIRSize(getModuleIRSize()), CurrentIRSize(InitialIRSize) {
142+
InitialIRSize(getModuleIRSize()), CurrentIRSize(InitialIRSize),
143+
PSI(MAM.getResult<ProfileSummaryAnalysis>(M)) {
133144
assert(ModelRunner);
134145
ModelRunner->switchContext("");
135146
// Extract the 'call site height' feature - the position of a call site
@@ -334,6 +345,11 @@ std::unique_ptr<InlineAdvice> MLInlineAdvisor::getAdviceImpl(CallBase &CB) {
334345
auto &TIR = FAM.getResult<TargetIRAnalysis>(Callee);
335346
auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(Caller);
336347

348+
if (SkipPolicy == SkipMLPolicyCriteria::IfCallerIsNotCold) {
349+
if (!PSI.isFunctionEntryCold(&Caller))
350+
return std::make_unique<InlineAdvice>(this, CB, ORE,
351+
GetDefaultAdvice(CB));
352+
}
337353
auto MandatoryKind = InlineAdvisor::getMandatoryKind(CB, FAM, ORE);
338354
// If this is a "never inline" case, there won't be any changes to internal
339355
// state we need to track, so we can just return the base InlineAdvice, which

llvm/lib/Analysis/models/gen-inline-oz-test-model.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,12 @@ def get_output_spec_path(path):
102102
return os.path.join(path, "output_spec.json")
103103

104104

105-
def build_mock_model(path, signature):
105+
def build_mock_model(path, signature, advice):
106106
"""Build and save the mock model with the given signature"""
107107
module = tf.Module()
108108

109109
def action(*inputs):
110-
return {signature["output"]: tf.constant(value=1, dtype=tf.int64)}
110+
return {signature["output"]: tf.constant(value=advice, dtype=tf.int64)}
111111

112112
module.action = tf.function()(action)
113113
action = {"action": module.action.get_concrete_function(signature["inputs"])}
@@ -128,12 +128,18 @@ def get_signature():
128128

129129

130130
def main(argv):
131-
assert len(argv) == 2
131+
assert len(argv) == 2 or (len(argv) == 3 and argv[2] == "never")
132132
model_path = argv[1]
133133

134134
print(f"Output model to: [{argv[1]}]")
135+
136+
constant_advice = 1
137+
if len(argv) == 3:
138+
constant_advice = 0
139+
print(f"The model will always return: {constant_advice}")
140+
135141
signature = get_signature()
136-
build_mock_model(model_path, signature)
142+
build_mock_model(model_path, signature, constant_advice)
137143

138144

139145
if __name__ == "__main__":
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
; REQUIRES: have_tflite
2+
; RUN: rm -rf %t.runfiles %t.tflite %t.model_out
3+
; RUN: mkdir %t.runfiles
4+
; RUN: cp %S/../../../../lib/Analysis/models/gen-inline-oz-test-model.py %t.runfiles
5+
; RUN: cp %S/../../../../lib/Analysis/models/saved-model-to-tflite.py %t.runfiles
6+
; RUN: %python %t.runfiles/gen-inline-oz-test-model.py %t.model_out never
7+
; RUN: %python %t.runfiles/saved-model-to-tflite.py %t.model_out %t.tflite
8+
9+
; When running O2, we expect both callers to inline callee.
10+
; RUN: opt < %s -passes='default<O2>' -inline-threshold=0 -hot-callsite-threshold=100 -S | FileCheck %s --check-prefixes=O2-HOT,O2-COLD
11+
12+
; The ML model we use always blocks inlining (by construction)
13+
; RUN: opt < %s -passes='default<O2>' -inline-threshold=0 -hot-callsite-threshold=100 \
14+
; RUN: -enable-ml-inliner=development -ml-inliner-model-under-training=%t.tflite \
15+
; RUN: -S | FileCheck %s --check-prefixes=ML-HOT,ML-COLD
16+
17+
; When bypassing ML for non-cold callers, the hot caller will have its callee inlined, but the cold one won't
18+
; RUN: opt < %s -passes='default<O2>' -inline-threshold=0 -hot-callsite-threshold=100 \
19+
; RUN: -enable-ml-inliner=development -ml-inliner-model-under-training=%t.tflite \
20+
; RUN: -ml-inliner-skip-policy=if-caller-not-cold -S | FileCheck %s --check-prefixes=O2-HOT,ML-COLD
21+
22+
declare void @extern()
23+
24+
define i32 @callee(i32 %x) {
25+
%x1 = add i32 %x, 1
26+
%x2 = add i32 %x1, 1
27+
%x3 = add i32 %x2, 1
28+
call void @extern()
29+
call void @extern()
30+
ret i32 %x3
31+
}
32+
33+
define i32 @hot_caller(i32 %y1) !prof !15 {
34+
%y = call i32 @callee(i32 %y1), !prof !16
35+
ret i32 %y
36+
}
37+
38+
define i32 @cold_caller(i32 %y1) !prof !17 {
39+
%y = call i32 @callee(i32 %y1), !prof !16
40+
ret i32 %y
41+
}
42+
43+
44+
!llvm.module.flags = !{!1}
45+
!15 = !{!"function_entry_count", i64 300}
46+
!16 = !{!"branch_weights", i64 300}
47+
!17 = !{!"function_entry_count", i64 1}
48+
49+
!1 = !{i32 1, !"ProfileSummary", !2}
50+
!2 = !{!3, !4, !5, !6, !7, !8, !9, !10}
51+
!3 = !{!"ProfileFormat", !"SampleProfile"}
52+
!4 = !{!"TotalCount", i64 10000}
53+
!5 = !{!"MaxCount", i64 1000}
54+
!6 = !{!"MaxInternalCount", i64 1}
55+
!7 = !{!"MaxFunctionCount", i64 1000}
56+
!8 = !{!"NumCounts", i64 3}
57+
!9 = !{!"NumFunctions", i64 3}
58+
!10 = !{!"DetailedSummary", !11}
59+
!11 = !{!12, !13, !14}
60+
!12 = !{i32 10000, i64 100, i32 1}
61+
!13 = !{i32 999000, i64 100, i32 1}
62+
!14 = !{i32 999999, i64 1, i32 2}
63+
64+
; O2-HOT-LABEL: @hot_caller
65+
; O2-HOT-NOT: call i32 @callee
66+
; O2-HOT: call void @extern
67+
; O2-HOT-NEXT: call void @extern
68+
; O2-HOT-NEXT: ret
69+
; O2-COLD-LABEL: @cold_caller
70+
; O2-COLD-NOT: call i32 @callee
71+
; O2-COLD: call void @extern
72+
; O2-COLD-NEXT: call void @extern
73+
; O2-COLD-NEXT: ret
74+
75+
; ML-HOT-LABEL: @hot_caller
76+
; ML-HOT-NEXT: call i32 @callee
77+
; ML-COLD-LABEL: @cold_caller
78+
; ML-COLD-NEXT: call i32 @callee

0 commit comments

Comments
 (0)