Skip to content

Commit c8cf393

Browse files
authored
[mlgo][inliner] Handle recursive cases when skipping non-cold functions (#164099)
The `MLInlineAdvisor`​ currently skips over recursive cases, except that when we delegate to the default policy for non-cold functions, that policy could allow such inlining. The code updating internal state afterwards needs to handle that case. Fix for https://issues.chromium.org/issues/369637577#comment14
1 parent b9f9b3b commit c8cf393

File tree

2 files changed

+69
-23
lines changed

2 files changed

+69
-23
lines changed

llvm/lib/Analysis/MLInlineAdvisor.cpp

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -324,32 +324,44 @@ void MLInlineAdvisor::onSuccessfulInlining(const MLInlineAdvice &Advice,
324324
FAM.invalidate(*Caller, PA);
325325
}
326326
Advice.updateCachedCallerFPI(FAM);
327-
int64_t IRSizeAfter =
328-
getIRSize(*Caller) + (CalleeWasDeleted ? 0 : Advice.CalleeIRSize);
329-
CurrentIRSize += IRSizeAfter - (Advice.CallerIRSize + Advice.CalleeIRSize);
327+
if (Caller == Callee) {
328+
assert(!CalleeWasDeleted);
329+
// We double-counted CallerAndCalleeEdges - since the caller and callee
330+
// would be the same
331+
assert(Advice.CallerAndCalleeEdges % 2 == 0);
332+
CurrentIRSize += getIRSize(*Caller) - Advice.CallerIRSize;
333+
EdgeCount += getCachedFPI(*Caller).DirectCallsToDefinedFunctions -
334+
Advice.CallerAndCalleeEdges / 2;
335+
// The NodeCount would stay the same.
336+
} else {
337+
int64_t IRSizeAfter =
338+
getIRSize(*Caller) + (CalleeWasDeleted ? 0 : Advice.CalleeIRSize);
339+
CurrentIRSize += IRSizeAfter - (Advice.CallerIRSize + Advice.CalleeIRSize);
340+
341+
// We can delta-update module-wide features. We know the inlining only
342+
// changed the caller, and maybe the callee (by deleting the latter). Nodes
343+
// are simple to update. For edges, we 'forget' the edges that the caller
344+
// and callee used to have before inlining, and add back what they currently
345+
// have together.
346+
int64_t NewCallerAndCalleeEdges =
347+
getCachedFPI(*Caller).DirectCallsToDefinedFunctions;
348+
349+
// A dead function's node is not actually removed from the call graph until
350+
// the end of the call graph walk, but the node no longer belongs to any
351+
// valid SCC.
352+
if (CalleeWasDeleted) {
353+
--NodeCount;
354+
NodesInLastSCC.erase(CG.lookup(*Callee));
355+
DeadFunctions.insert(Callee);
356+
} else {
357+
NewCallerAndCalleeEdges +=
358+
getCachedFPI(*Callee).DirectCallsToDefinedFunctions;
359+
}
360+
EdgeCount += (NewCallerAndCalleeEdges - Advice.CallerAndCalleeEdges);
361+
}
330362
if (CurrentIRSize > SizeIncreaseThreshold * InitialIRSize)
331363
ForceStop = true;
332364

333-
// We can delta-update module-wide features. We know the inlining only changed
334-
// the caller, and maybe the callee (by deleting the latter).
335-
// Nodes are simple to update.
336-
// For edges, we 'forget' the edges that the caller and callee used to have
337-
// before inlining, and add back what they currently have together.
338-
int64_t NewCallerAndCalleeEdges =
339-
getCachedFPI(*Caller).DirectCallsToDefinedFunctions;
340-
341-
// A dead function's node is not actually removed from the call graph until
342-
// the end of the call graph walk, but the node no longer belongs to any valid
343-
// SCC.
344-
if (CalleeWasDeleted) {
345-
--NodeCount;
346-
NodesInLastSCC.erase(CG.lookup(*Callee));
347-
DeadFunctions.insert(Callee);
348-
} else {
349-
NewCallerAndCalleeEdges +=
350-
getCachedFPI(*Callee).DirectCallsToDefinedFunctions;
351-
}
352-
EdgeCount += (NewCallerAndCalleeEdges - Advice.CallerAndCalleeEdges);
353365
assert(CurrentIRSize >= 0 && EdgeCount >= 0 && NodeCount >= 0);
354366
}
355367

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 6
2+
; REQUIRES: llvm_inliner_model_autogenerated
3+
; RUN: opt -S %s -o - -passes='inliner-ml-advisor-release' -ml-inliner-skip-policy=if-caller-not-cold | FileCheck %s
4+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
5+
target triple = "aarch64-unknown-linux-android29"
6+
7+
define i32 @a_func(ptr %this, i32 %color_id, i1 %dark_mode) local_unnamed_addr {
8+
; CHECK-LABEL: define i32 @a_func(
9+
; CHECK-SAME: ptr [[THIS:%.*]], i32 [[COLOR_ID:%.*]], i1 [[DARK_MODE:%.*]]) local_unnamed_addr {
10+
; CHECK-NEXT: [[ENTRY:.*:]]
11+
; CHECK-NEXT: br i1 [[DARK_MODE]], label %[[SW_BB97:.*]], label %[[COMMON_RET:.*]]
12+
; CHECK: [[COMMON_RET]]:
13+
; CHECK-NEXT: ret i32 0
14+
; CHECK: [[SW_BB97]]:
15+
; CHECK-NEXT: br label %[[COMMON_RET]]
16+
;
17+
entry:
18+
br i1 %dark_mode, label %sw.bb97, label %common.ret
19+
20+
common.ret: ; preds = %sw.bb97, %entry
21+
ret i32 0
22+
23+
sw.bb97: ; preds = %entry
24+
%call.i = tail call i32 @a_func(ptr null, i32 0, i1 false)
25+
br label %common.ret
26+
}
27+
28+
; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: read)
29+
declare ptr @llvm.load.relative.i32(ptr %0, i32 %1) #0
30+
31+
attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: read) }
32+
;.
33+
; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: read) }
34+
;.

0 commit comments

Comments
 (0)