mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 11:13:28 +01:00
[PM/Inliner] Add a test case that encapsulates the core issue addressed
in r297374. I've extracted a small version of this from the C++ metaprogram Richard came up with to exercise these kinds of issues and written comments to describe both how to reproduce a fresh version of the test case and what likely failure modes are. The test case is still a bit brittle as it depends on the particular inline cost modeling and SCC visitation order, but it definitely would have caught the bug right away when developing things so it seems a really valuable test case to have. llvm-svn: 297935
This commit is contained in:
parent
43ec62fd2b
commit
5dc13f0626
460
test/Transforms/Inline/monster_scc.ll
Normal file
460
test/Transforms/Inline/monster_scc.ll
Normal file
@ -0,0 +1,460 @@
|
||||
; This test creates a monster SCC with a very pernicious call graph. It builds
|
||||
; a cycle of cross-connected pairs of functions with interesting inlining
|
||||
; decisions throughout, but ultimately trivial code complexity.
|
||||
;
|
||||
; Typically, a greedy approach to inlining works well for bottom-up inliners
|
||||
; such as LLVM's. However, there is no way to be bottom-up over an SCC: it's
|
||||
; a cycle! Greedily inlining as much as possible into each function of this
|
||||
; *SCC* will have the disasterous effect of inlining all N-1 functions into the
|
||||
; first one visited, N-2 functions into the second one visited, N-3 into the
|
||||
; third, and so on. This is because until inlining occurs, each function in
|
||||
; isolation appears to be an excellent inline candidate.
|
||||
;
|
||||
; Note that the exact number of calls in each function doesn't really matter.
|
||||
; It is mostly a function of cost thresholds and visit order. Because this is an
|
||||
; SCC there is no "right" or "wrong" answer here as long as no function blows up
|
||||
; to be *huge*. The specific concerning pattern is if one or more functions get
|
||||
; more than 16 calls in them.
|
||||
;
|
||||
; This test is extracted from the following C++ program compiled with Clang.
|
||||
; The IR is simplified with SROA, instcombine, and simplify-cfg. Then C++
|
||||
; linkage stuff, attributes, target specific things, metadata and comments were
|
||||
; removed. The order of the fuctions is also made more predictable than Clang's
|
||||
; output order.
|
||||
;
|
||||
; void g(int);
|
||||
;
|
||||
; template <bool K, int N> void f(bool *B, bool *E) {
|
||||
; if (K)
|
||||
; g(N);
|
||||
; if (B == E)
|
||||
; return;
|
||||
; if (*B)
|
||||
; f<true, N + 1>(B + 1, E);
|
||||
; else
|
||||
; f<false, N + 1>(B + 1, E);
|
||||
; }
|
||||
; template <> void f<false, MAX>(bool *B, bool *E) { return f<false, 0>(B, E); }
|
||||
; template <> void f<true, MAX>(bool *B, bool *E) { return f<true, 0>(B, E); }
|
||||
;
|
||||
; void test(bool *B, bool *E) { f<false, 0>(B, E); }
|
||||
;
|
||||
; RUN: opt -S < %s -inline -inline-threshold=150 | FileCheck %s --check-prefixes=CHECK,OLD
|
||||
; RUN: opt -S < %s -passes=inline -inline-threshold=150 | FileCheck %s --check-prefixes=CHECK,NEW
|
||||
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
|
||||
declare void @_Z1gi(i32)
|
||||
|
||||
; CHECK-LABEL: define void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1gi(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb1ELi2EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb0ELi2EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb0ELi1EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1gi(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb1ELi2EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi2EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb1ELi2EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi2EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
define void @_Z1fILb0ELi0EEvPbS0_(i8* %B, i8* %E) {
|
||||
entry:
|
||||
%cmp = icmp eq i8* %B, %E
|
||||
br i1 %cmp, label %if.end3, label %if.end
|
||||
|
||||
if.end:
|
||||
%0 = load i8, i8* %B, align 1
|
||||
%tobool = icmp eq i8 %0, 0
|
||||
%add.ptr2 = getelementptr inbounds i8, i8* %B, i64 1
|
||||
br i1 %tobool, label %if.else, label %if.then1
|
||||
|
||||
if.then1:
|
||||
call void @_Z1fILb1ELi1EEvPbS0_(i8* %add.ptr2, i8* %E)
|
||||
br label %if.end3
|
||||
|
||||
if.else:
|
||||
call void @_Z1fILb0ELi1EEvPbS0_(i8* %add.ptr2, i8* %E)
|
||||
br label %if.end3
|
||||
|
||||
if.end3:
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @_Z1fILb1ELi0EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1gi(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1gi(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb1ELi2EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb0ELi2EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb0ELi1EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1gi(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb1ELi1EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb1ELi2EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi2EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
define void @_Z1fILb1ELi0EEvPbS0_(i8* %B, i8* %E) {
|
||||
entry:
|
||||
call void @_Z1gi(i32 0)
|
||||
%cmp = icmp eq i8* %B, %E
|
||||
br i1 %cmp, label %if.end3, label %if.end
|
||||
|
||||
if.end:
|
||||
%0 = load i8, i8* %B, align 1
|
||||
%tobool = icmp eq i8 %0, 0
|
||||
%add.ptr2 = getelementptr inbounds i8, i8* %B, i64 1
|
||||
br i1 %tobool, label %if.else, label %if.then1
|
||||
|
||||
if.then1:
|
||||
call void @_Z1fILb1ELi1EEvPbS0_(i8* %add.ptr2, i8* %E)
|
||||
br label %if.end3
|
||||
|
||||
if.else:
|
||||
call void @_Z1fILb0ELi1EEvPbS0_(i8* %add.ptr2, i8* %E)
|
||||
br label %if.end3
|
||||
|
||||
if.end3:
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @_Z1fILb0ELi1EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1gi(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1gi(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb1ELi0EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb1ELi0EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb0ELi2EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb1ELi2EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1gi(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb1ELi0EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi3EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
define void @_Z1fILb0ELi1EEvPbS0_(i8* %B, i8* %E) {
|
||||
entry:
|
||||
%cmp = icmp eq i8* %B, %E
|
||||
br i1 %cmp, label %if.end3, label %if.end
|
||||
|
||||
if.end:
|
||||
%0 = load i8, i8* %B, align 1
|
||||
%tobool = icmp eq i8 %0, 0
|
||||
%add.ptr2 = getelementptr inbounds i8, i8* %B, i64 1
|
||||
br i1 %tobool, label %if.else, label %if.then1
|
||||
|
||||
if.then1:
|
||||
call void @_Z1fILb1ELi2EEvPbS0_(i8* %add.ptr2, i8* %E)
|
||||
br label %if.end3
|
||||
|
||||
if.else:
|
||||
call void @_Z1fILb0ELi2EEvPbS0_(i8* %add.ptr2, i8* %E)
|
||||
br label %if.end3
|
||||
|
||||
if.end3:
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @_Z1fILb1ELi1EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1gi(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb1ELi2EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb0ELi2EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1gi(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1gi(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1gi(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb1ELi0EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi3EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1gi(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb1ELi0EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi3EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
define void @_Z1fILb1ELi1EEvPbS0_(i8* %B, i8* %E) {
|
||||
entry:
|
||||
call void @_Z1gi(i32 1)
|
||||
%cmp = icmp eq i8* %B, %E
|
||||
; CHECK-NOT: call
|
||||
br i1 %cmp, label %if.end3, label %if.end
|
||||
|
||||
if.end:
|
||||
%0 = load i8, i8* %B, align 1
|
||||
%tobool = icmp eq i8 %0, 0
|
||||
%add.ptr2 = getelementptr inbounds i8, i8* %B, i64 1
|
||||
br i1 %tobool, label %if.else, label %if.then1
|
||||
|
||||
if.then1:
|
||||
call void @_Z1fILb1ELi2EEvPbS0_(i8* %add.ptr2, i8* %E)
|
||||
br label %if.end3
|
||||
|
||||
if.else:
|
||||
call void @_Z1fILb0ELi2EEvPbS0_(i8* %add.ptr2, i8* %E)
|
||||
br label %if.end3
|
||||
|
||||
if.end3:
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @_Z1fILb0ELi2EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1gi(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb1ELi0EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb1ELi0EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1gi(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb1ELi0EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb1ELi4EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
define void @_Z1fILb0ELi2EEvPbS0_(i8* %B, i8* %E) {
|
||||
entry:
|
||||
%cmp = icmp eq i8* %B, %E
|
||||
br i1 %cmp, label %if.end3, label %if.end
|
||||
|
||||
if.end:
|
||||
%0 = load i8, i8* %B, align 1
|
||||
%tobool = icmp eq i8 %0, 0
|
||||
%add.ptr2 = getelementptr inbounds i8, i8* %B, i64 1
|
||||
br i1 %tobool, label %if.else, label %if.then1
|
||||
|
||||
if.then1:
|
||||
call void @_Z1fILb1ELi3EEvPbS0_(i8* %add.ptr2, i8* %E)
|
||||
br label %if.end3
|
||||
|
||||
if.else:
|
||||
call void @_Z1fILb0ELi3EEvPbS0_(i8* %add.ptr2, i8* %E)
|
||||
br label %if.end3
|
||||
|
||||
if.end3:
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @_Z1fILb1ELi2EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1gi(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1gi(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb1ELi0EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb1ELi0EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1gi(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1gi(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1gi(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb1ELi1EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi1EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1gi(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb1ELi1EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi1EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
define void @_Z1fILb1ELi2EEvPbS0_(i8* %B, i8* %E) {
|
||||
entry:
|
||||
call void @_Z1gi(i32 2)
|
||||
%cmp = icmp eq i8* %B, %E
|
||||
br i1 %cmp, label %if.end3, label %if.end
|
||||
|
||||
if.end:
|
||||
%0 = load i8, i8* %B, align 1
|
||||
%tobool = icmp eq i8 %0, 0
|
||||
%add.ptr2 = getelementptr inbounds i8, i8* %B, i64 1
|
||||
br i1 %tobool, label %if.else, label %if.then1
|
||||
|
||||
if.then1:
|
||||
call void @_Z1fILb1ELi3EEvPbS0_(i8* %add.ptr2, i8* %E)
|
||||
br label %if.end3
|
||||
|
||||
if.else:
|
||||
call void @_Z1fILb0ELi3EEvPbS0_(i8* %add.ptr2, i8* %E)
|
||||
br label %if.end3
|
||||
|
||||
if.end3:
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @_Z1fILb0ELi3EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb1ELi0EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1gi(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb1ELi1EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi1EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
define void @_Z1fILb0ELi3EEvPbS0_(i8* %B, i8* %E) {
|
||||
entry:
|
||||
%cmp = icmp eq i8* %B, %E
|
||||
br i1 %cmp, label %if.end3, label %if.end
|
||||
|
||||
if.end:
|
||||
%0 = load i8, i8* %B, align 1
|
||||
%tobool = icmp eq i8 %0, 0
|
||||
%add.ptr2 = getelementptr inbounds i8, i8* %B, i64 1
|
||||
br i1 %tobool, label %if.else, label %if.then1
|
||||
|
||||
if.then1:
|
||||
call void @_Z1fILb1ELi4EEvPbS0_(i8* %add.ptr2, i8* %E)
|
||||
br label %if.end3
|
||||
|
||||
if.else:
|
||||
call void @_Z1fILb0ELi4EEvPbS0_(i8* %add.ptr2, i8* %E)
|
||||
br label %if.end3
|
||||
|
||||
if.end3:
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @_Z1fILb1ELi3EEvPbS0_(
|
||||
; CHECK-NOT: call
|
||||
; CHECK: call void @_Z1gi(
|
||||
; CHECK-NOT: call
|
||||
; CHECK: call void @_Z1fILb1ELi0EEvPbS0_(
|
||||
; CHECK-NOT: call
|
||||
; CHECK: call void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; CHECK-NOT: call
|
||||
define void @_Z1fILb1ELi3EEvPbS0_(i8* %B, i8* %E) {
|
||||
entry:
|
||||
call void @_Z1gi(i32 3)
|
||||
%cmp = icmp eq i8* %B, %E
|
||||
br i1 %cmp, label %if.end3, label %if.end
|
||||
|
||||
if.end:
|
||||
%0 = load i8, i8* %B, align 1
|
||||
%tobool = icmp eq i8 %0, 0
|
||||
%add.ptr2 = getelementptr inbounds i8, i8* %B, i64 1
|
||||
br i1 %tobool, label %if.else, label %if.then1
|
||||
|
||||
if.then1:
|
||||
call void @_Z1fILb1ELi4EEvPbS0_(i8* %add.ptr2, i8* %E)
|
||||
br label %if.end3
|
||||
|
||||
if.else:
|
||||
call void @_Z1fILb0ELi4EEvPbS0_(i8* %add.ptr2, i8* %E)
|
||||
br label %if.end3
|
||||
|
||||
if.end3:
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @_Z1fILb0ELi4EEvPbS0_(
|
||||
; CHECK-NOT: call
|
||||
; CHECK: call void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; CHECK-NOT: call
|
||||
define void @_Z1fILb0ELi4EEvPbS0_(i8* %B, i8* %E) {
|
||||
entry:
|
||||
call void @_Z1fILb0ELi0EEvPbS0_(i8* %B, i8* %E)
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @_Z1fILb1ELi4EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; OLD: call void @_Z1fILb1ELi0EEvPbS0_(
|
||||
; OLD-NOT: call
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1gi(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb1ELi1EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb1ELi2EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1gi(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb1ELi0EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi0EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
; NEW: call void @_Z1fILb0ELi3EEvPbS0_(
|
||||
; NEW-NOT: call
|
||||
define void @_Z1fILb1ELi4EEvPbS0_(i8* %B, i8* %E) {
|
||||
entry:
|
||||
call void @_Z1fILb1ELi0EEvPbS0_(i8* %B, i8* %E)
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK-LABEL: define void @_Z4testPbS_(
|
||||
; CHECK: call
|
||||
; CHECK-NOT: call
|
||||
define void @_Z4testPbS_(i8* %B, i8* %E) {
|
||||
entry:
|
||||
call void @_Z1fILb0ELi0EEvPbS0_(i8* %B, i8* %E)
|
||||
ret void
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user