llvm-project/llvm/test/Transforms/Inline/inline-history.ll
Arthur Eubanks 82505fbfc8
[Inliner] Put inline history into IR as !inline_history metadata (#190700)
(Reland of #190092 with verifier change to look through GlobalAliases)

So that it's preserved across all inline invocations rather than just
one inliner pass run.

This prevents cases where devirtualization in the simplification
pipeline uncovers inlining opportunities that should be discarded due to
inline history, but we dropped the inline history between inliner pass
runs, causing code size to blow up, sometimes exponentially.

For compile time reasons, we want to limit this to only call sites that
have the potential to inline through SCCs, potentially with the help of
devirtualization. This means that the callee is in a non-trivial
(Ref)SCC, or the call site was previously an indirect call, which can
potentially be devirtualized to call any function.

The CGSCCUpdater::InlinedInternalEdges logic still seems to be relevant
even with this change, as monster_scc.ll blows up if I remove that code.


http://llvm-compile-time-tracker.com/compare.php?from=e830d88e8ae5f44a97cc76136a0a4e83aa9157c0&to=ed535e732fc41b79ab8efda2417886cbd0812f7f&stat=instructions:u

Fixes #186926.
2026-04-06 17:31:43 -07:00

103 lines
2.3 KiB
LLVM

; RUN: opt -passes=inline -S < %s | FileCheck %s
declare ptr @get()
declare ptr @foo1()
declare ptr @foo2()
; Don't need to track inline history for non-mutually recursive calls.
define internal void @x() noinline {
ret void
}
define internal void @y() {
call void @x()
ret void
}
define void @z() {
; CHECK-LABEL: define void @z() {
; CHECK-NEXT: call void @x(){{$}}
; CHECK-NEXT: ret void
call void @y()
ret void
}
; Indirect calls may be devirtualized, they need inline history tracking. In
; this case, @s is the history, but it gets deleted so the inline history
; metadata becomes null. This is fine since we cannot infinitely inline through
; @s anymore as it doesn't exist.
define internal void @s() {
%p = call ptr @get()
call void %p()
ret void
}
define void @t() {
; CHECK-LABEL: define void @t() {
; CHECK-NEXT: %p.i = call ptr @get()
; CHECK-NEXT: call void %p.i(), !inline_history [[HISTORY_T:![0-9]+]]
; CHECK-NEXT: ret void
call void @s()
ret void
}
; This will inline @f1 into @a, causing two new calls to @f2, which will get inlined for two calls to @f1.
; The inline history should stop recursive inlining here.
define internal void @f1(ptr %p) {
call void %p(ptr @f1)
ret void
}
define internal void @f2(ptr %p) {
call void %p(ptr @f2)
call void %p(ptr @f2)
ret void
}
define void @b() {
; CHECK-LABEL: define void @b() {
; CHECK-NEXT: call void @f1(ptr @f2), !inline_history [[HISTORY_B:![0-9]+]]
; CHECK-NEXT: call void @f1(ptr @f2), !inline_history [[HISTORY_B:![0-9]+]]
; CHECK-NEXT: ret void
;
call void @a()
ret void
}
define internal void @a() {
call void @f1(ptr @f2)
ret void
}
; Check that the inline history is
; {callee, processed call's inline_history, just-inlined call's inline_history}
define void @m(ptr %p) noinline {
ret void
}
define void @n() {
call void @m(ptr @n2), !inline_history !{ptr @foo1}
ret void
}
define void @n2() {
call void @m(ptr @n)
ret void
}
define void @o() {
; CHECK-LABEL: define void @o() {
; CHECK-NEXT: call void @m(ptr @n2), !inline_history [[HISTORY_O:![0-9]+]]
; CHECK-NEXT: ret void
call void @n(), !inline_history !{ptr @foo2}
ret void
}
; CHECK-DAG: [[HISTORY_T]] = distinct !{null}
; CHECK-DAG: [[HISTORY_B]] = !{ptr @f2, ptr @f1}
; CHECK-DAG: [[HISTORY_O]] = !{ptr @n, ptr @foo2, ptr @foo1}