mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 18:54:02 +01:00
[LazyCallGraph] Build SCCs of the reference graph in order
``` // The legacy PM CGPassManager discovers SCCs this way: for function in the source order tarjanSCC(function) // While the new PM CGSCCPassManager does: for function in the reversed source order [1] discover a reference graph SCC build call graph SCCs inside the reference graph SCC ``` In the common cases, reference graph ~= call graph, the new PM order is undesired because for `a | b | c` (3 independent functions), the new PM will process them in the reversed order: c, b, a. If `a <-> b <-> c`, we can see that `-print-after-all` will report the sole SCC as `scc: (c, b, a)`. This patch corrects the iteration order. The discovered SCC order will match the legacy PM in the common cases. For some tests (`Transforms/Inline/cgscc-*.ll` and `unittests/Analysis/CGSCCPassManagerTest.cpp`), the behaviors are dependent on the SCC discovery order and there are too many check lines for the particular order. This patch simply reverses the function order to avoid changing too many check lines. Differential Revision: https://reviews.llvm.org/D90566
This commit is contained in:
parent
4adb5d4209
commit
c9829bfb08
@ -1729,10 +1729,7 @@ void LazyCallGraph::buildRefSCCs() {
|
||||
for (Edge &E : *this)
|
||||
Roots.push_back(&E.getNode());
|
||||
|
||||
// The roots will be popped of a stack, so use reverse to get a less
|
||||
// surprising order. This doesn't change any of the semantics anywhere.
|
||||
std::reverse(Roots.begin(), Roots.end());
|
||||
|
||||
// The roots will be iterated in order.
|
||||
buildGenericSCCs(
|
||||
Roots,
|
||||
[](Node &N) {
|
||||
|
@ -10,11 +10,11 @@ target triple = "x86_64-grtev4-linux-gnu"
|
||||
|
||||
; CHECK: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: bar
|
||||
|
||||
; CHECK-NEXT: foo
|
||||
; CHECK-EMPTY:
|
||||
; CHECK: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: foo
|
||||
; CHECK-NEXT: bar
|
||||
|
||||
; CHECK-NOT: baz
|
||||
|
||||
|
@ -256,10 +256,74 @@ entry:
|
||||
; Verify the SCCs formed.
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f1
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f2
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f3
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f4
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f5
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f6
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f7
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f8
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f9
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f10
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f11
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f12
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: test0
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: test1
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: test2
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 3 functions:
|
||||
; CHECK-NEXT: test3_ca2
|
||||
; CHECK-NEXT: test3_ca3
|
||||
; CHECK-NEXT: test3_ca1
|
||||
; CHECK-NEXT: test3_ca2
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 2 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
@ -269,75 +333,11 @@ entry:
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 3 call SCCs:
|
||||
; CHECK-NEXT: SCC with 2 functions:
|
||||
; CHECK-NEXT: test3_ac2
|
||||
; CHECK-NEXT: test3_ac1
|
||||
; CHECK-NEXT: test3_ac2
|
||||
; CHECK-NEXT: SCC with 2 functions:
|
||||
; CHECK-NEXT: test3_ab2
|
||||
; CHECK-NEXT: test3_ab1
|
||||
; CHECK-NEXT: SCC with 2 functions:
|
||||
; CHECK-NEXT: test3_aa2
|
||||
; CHECK-NEXT: test3_aa1
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f7
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f6
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f5
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f4
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f3
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f2
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f1
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: test2
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f10
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f12
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f11
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f9
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f8
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: test1
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: f
|
||||
;
|
||||
; CHECK-LABEL: RefSCC with 1 call SCCs:
|
||||
; CHECK-NEXT: SCC with 1 functions:
|
||||
; CHECK-NEXT: test0
|
||||
; CHECK-NEXT: test3_aa2
|
||||
|
@ -1,27 +1,41 @@
|
||||
; RUN: opt < %s 2>&1 -disable-output \
|
||||
; RUN: -inline -print-after-all | FileCheck %s -check-prefix=INL
|
||||
; RUN: -inline -print-after-all | FileCheck %s --check-prefix=LEGACY
|
||||
; RUN: opt < %s 2>&1 -disable-output \
|
||||
; RUN: -passes=inline -print-after-all | FileCheck %s -check-prefix=INL
|
||||
; RUN: opt < %s 2>&1 -disable-output \
|
||||
; RUN: -passes=inliner-wrapper -print-after-all | FileCheck %s -check-prefix=INL
|
||||
; RUN: opt < %s 2>&1 -disable-output \
|
||||
; RUN: -inline -print-after-all -print-module-scope | FileCheck %s -check-prefix=INL-MOD
|
||||
; RUN: -inline -print-after-all -print-module-scope | FileCheck %s -check-prefix=LEGACY-MOD
|
||||
; RUN: opt < %s 2>&1 -disable-output \
|
||||
; RUN: -passes=inline -print-after-all -print-module-scope | FileCheck %s -check-prefix=INL-MOD
|
||||
; RUN: opt < %s 2>&1 -disable-output \
|
||||
; RUN: -passes=inliner-wrapper -print-after-all -print-module-scope | FileCheck %s -check-prefix=INL-MOD
|
||||
|
||||
; INL: IR Dump After {{Function Integration/Inlining|InlinerPass .*scc: .bar, foo}}
|
||||
; INL: define void @bar()
|
||||
; INL-NEXT: call void @foo()
|
||||
; LEGACY: IR Dump After Function Integration/Inlining
|
||||
; LEGACY: define void @bar()
|
||||
; LEGACY-NEXT: call void @foo()
|
||||
; LEGACY: define void @foo()
|
||||
; LEGACY-NEXT: call void @bar()
|
||||
; LEGACY: IR Dump After Function Integration/Inlining
|
||||
; LEGACY: define void @tester()
|
||||
; LEGACY-NEXT: call void @foo()
|
||||
|
||||
; INL: IR Dump After InlinerPass *** (scc: (foo, bar))
|
||||
; INL: define void @foo()
|
||||
; INL-NEXT: call void @bar()
|
||||
; INL: IR Dump After {{Function Integration/Inlining|InlinerPass .*scc: .tester}}
|
||||
; INL: define void @bar()
|
||||
; INL-NEXT: call void @foo()
|
||||
; INL: IR Dump After InlinerPass *** (scc: (tester))
|
||||
; INL: define void @tester()
|
||||
; INL-NEXT: call void @foo()
|
||||
; INL: IR Dump After
|
||||
|
||||
; INL-MOD: IR Dump After {{Function Integration/Inlining|InlinerPass .*scc: .bar, foo}}
|
||||
; LEGACY-MOD: IR Dump After Function Integration/Inlining
|
||||
; LEGACY-MOD-NEXT: ModuleID =
|
||||
; LEGACY-MOD: define void @tester()
|
||||
; LEGACY-MOD: define void @foo()
|
||||
; LEGACY-MOD: define void @bar()
|
||||
|
||||
; INL-MOD-LABEL:*** IR Dump After InlinerPass *** (scc: (foo, bar))
|
||||
; INL-MOD-NEXT: ModuleID =
|
||||
; INL-MOD-NEXT: source_filename =
|
||||
; INL-MOD: define void @tester()
|
||||
@ -30,7 +44,7 @@
|
||||
; INL-MOD-NEXT: call void @bar()
|
||||
; INL-MOD: define void @bar()
|
||||
; INL-MOD-NEXT: call void @foo()
|
||||
; INL-MOD: IR Dump After {{Function Integration/Inlining|InlinerPass .*scc: .tester}}
|
||||
; INL-MOD-LABEL:*** IR Dump After InlinerPass *** (scc: (tester))
|
||||
; INL-MOD-NEXT: ModuleID =
|
||||
; INL-MOD-NEXT: source_filename =
|
||||
; INL-MOD: define void @tester()
|
||||
|
@ -97,37 +97,21 @@ define void @fn_no_null_opt(i32* %P, i1 %C) null_pointer_is_valid {
|
||||
; IS__TUNIT____: exit:
|
||||
; IS__TUNIT____-NEXT: ret void
|
||||
;
|
||||
; IS__CGSCC_OPM: Function Attrs: nofree norecurse nosync nounwind null_pointer_is_valid
|
||||
; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@fn_no_null_opt
|
||||
; IS__CGSCC_OPM-SAME: (i32* nocapture nofree writeonly align 4 dereferenceable_or_null(4) [[P:%.*]], i1 [[C:%.*]]) [[ATTR2:#.*]] {
|
||||
; IS__CGSCC_OPM-NEXT: entry:
|
||||
; IS__CGSCC_OPM-NEXT: br label [[IF_END:%.*]]
|
||||
; IS__CGSCC_OPM: for.cond1:
|
||||
; IS__CGSCC_OPM-NEXT: br i1 [[C]], label [[IF_END]], label [[EXIT:%.*]]
|
||||
; IS__CGSCC_OPM: if.end:
|
||||
; IS__CGSCC_OPM-NEXT: [[E_2:%.*]] = phi i32* [ undef, [[ENTRY:%.*]] ], [ null, [[FOR_COND1:%.*]] ]
|
||||
; IS__CGSCC_OPM-NEXT: [[TMP0:%.*]] = load i32, i32* null, align 4
|
||||
; IS__CGSCC_OPM-NEXT: [[CALL:%.*]] = call i32 @fn0(i32 [[TMP0]])
|
||||
; IS__CGSCC_OPM-NEXT: store i32 [[CALL]], i32* [[P]], align 4
|
||||
; IS__CGSCC_OPM-NEXT: br label [[FOR_COND1]]
|
||||
; IS__CGSCC_OPM: exit:
|
||||
; IS__CGSCC_OPM-NEXT: ret void
|
||||
;
|
||||
; IS__CGSCC_NPM: Function Attrs: nofree norecurse nosync nounwind null_pointer_is_valid
|
||||
; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@fn_no_null_opt
|
||||
; IS__CGSCC_NPM-SAME: (i32* nocapture nofree writeonly align 4 dereferenceable_or_null(4) [[P:%.*]], i1 [[C:%.*]]) [[ATTR2:#.*]] {
|
||||
; IS__CGSCC_NPM-NEXT: entry:
|
||||
; IS__CGSCC_NPM-NEXT: br label [[IF_END:%.*]]
|
||||
; IS__CGSCC_NPM: for.cond1:
|
||||
; IS__CGSCC_NPM-NEXT: br i1 [[C]], label [[IF_END]], label [[EXIT:%.*]]
|
||||
; IS__CGSCC_NPM: if.end:
|
||||
; IS__CGSCC_NPM-NEXT: [[E_2:%.*]] = phi i32* [ undef, [[ENTRY:%.*]] ], [ null, [[FOR_COND1:%.*]] ]
|
||||
; IS__CGSCC_NPM-NEXT: [[TMP0:%.*]] = load i32, i32* null, align 536870912
|
||||
; IS__CGSCC_NPM-NEXT: [[CALL:%.*]] = call i32 @fn0(i32 [[TMP0]])
|
||||
; IS__CGSCC_NPM-NEXT: store i32 [[CALL]], i32* [[P]], align 4
|
||||
; IS__CGSCC_NPM-NEXT: br label [[FOR_COND1]]
|
||||
; IS__CGSCC_NPM: exit:
|
||||
; IS__CGSCC_NPM-NEXT: ret void
|
||||
; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind null_pointer_is_valid
|
||||
; IS__CGSCC____-LABEL: define {{[^@]+}}@fn_no_null_opt
|
||||
; IS__CGSCC____-SAME: (i32* nocapture nofree writeonly align 4 dereferenceable_or_null(4) [[P:%.*]], i1 [[C:%.*]]) [[ATTR2:#.*]] {
|
||||
; IS__CGSCC____-NEXT: entry:
|
||||
; IS__CGSCC____-NEXT: br label [[IF_END:%.*]]
|
||||
; IS__CGSCC____: for.cond1:
|
||||
; IS__CGSCC____-NEXT: br i1 [[C]], label [[IF_END]], label [[EXIT:%.*]]
|
||||
; IS__CGSCC____: if.end:
|
||||
; IS__CGSCC____-NEXT: [[E_2:%.*]] = phi i32* [ undef, [[ENTRY:%.*]] ], [ null, [[FOR_COND1:%.*]] ]
|
||||
; IS__CGSCC____-NEXT: [[TMP0:%.*]] = load i32, i32* null, align 4
|
||||
; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i32 @fn0(i32 [[TMP0]])
|
||||
; IS__CGSCC____-NEXT: store i32 [[CALL]], i32* [[P]], align 4
|
||||
; IS__CGSCC____-NEXT: br label [[FOR_COND1]]
|
||||
; IS__CGSCC____: exit:
|
||||
; IS__CGSCC____-NEXT: ret void
|
||||
;
|
||||
entry:
|
||||
br label %if.end
|
||||
|
@ -10,7 +10,7 @@
|
||||
; REQUIRES: asserts
|
||||
; RUN: opt < %s -passes='cgscc(inline)' -inline-threshold=500 -debug-only=cgscc -S 2>&1 | FileCheck %s
|
||||
|
||||
; CHECK: Running an SCC pass across the RefSCC: [(test1_c, test1_a, test1_b)]
|
||||
; CHECK: Running an SCC pass across the RefSCC: [(test1_a, test1_b, test1_c)]
|
||||
; CHECK: Enqueuing the existing SCC in the worklist:(test1_b)
|
||||
; CHECK: Enqueuing a newly formed SCC:(test1_c)
|
||||
; CHECK: Enqueuing a new RefSCC in the update worklist: [(test1_b)]
|
||||
|
@ -153,59 +153,6 @@ bb2:
|
||||
@a = global i64 0
|
||||
@b = global i64 0
|
||||
|
||||
define void @test3_c(i32 %i) {
|
||||
entry:
|
||||
%cmp = icmp eq i32 %i, 5
|
||||
br i1 %cmp, label %if.end, label %if.then
|
||||
|
||||
if.then: ; preds = %entry
|
||||
%call = tail call i64 @random()
|
||||
%t0 = load i64, i64* @a
|
||||
%add = add nsw i64 %t0, %call
|
||||
store i64 %add, i64* @a
|
||||
br label %if.end
|
||||
|
||||
if.end: ; preds = %entry, %if.then
|
||||
tail call void @test3_d(i32 %i)
|
||||
%t6 = load i64, i64* @a
|
||||
%add85 = add nsw i64 %t6, 1
|
||||
store i64 %add85, i64* @a
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i64 @random()
|
||||
|
||||
define void @test3_d(i32 %i) {
|
||||
entry:
|
||||
%cmp = icmp eq i32 %i, 5
|
||||
br i1 %cmp, label %if.end, label %if.then
|
||||
|
||||
if.then: ; preds = %entry
|
||||
%call = tail call i64 @random()
|
||||
%t0 = load i64, i64* @a
|
||||
%add = add nsw i64 %t0, %call
|
||||
store i64 %add, i64* @a
|
||||
br label %if.end
|
||||
|
||||
if.end: ; preds = %entry, %if.then
|
||||
tail call void @test3_c(i32 %i)
|
||||
tail call void @test3_b()
|
||||
%t6 = load i64, i64* @a
|
||||
%add79 = add nsw i64 %t6, 3
|
||||
store i64 %add79, i64* @a
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: noinline
|
||||
define void @test3_b() #0 {
|
||||
entry:
|
||||
tail call void @test3_a()
|
||||
%t0 = load i64, i64* @a
|
||||
%add = add nsw i64 %t0, 2
|
||||
store i64 %add, i64* @a
|
||||
ret void
|
||||
}
|
||||
|
||||
; Check test3_c is inlined into test3_a once and only once.
|
||||
; CHECK-LABEL: @test3_a(
|
||||
; CHECK: tail call void @test3_b()
|
||||
@ -229,4 +176,57 @@ entry:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Function Attrs: noinline
|
||||
define void @test3_b() #0 {
|
||||
entry:
|
||||
tail call void @test3_a()
|
||||
%t0 = load i64, i64* @a
|
||||
%add = add nsw i64 %t0, 2
|
||||
store i64 %add, i64* @a
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @test3_d(i32 %i) {
|
||||
entry:
|
||||
%cmp = icmp eq i32 %i, 5
|
||||
br i1 %cmp, label %if.end, label %if.then
|
||||
|
||||
if.then: ; preds = %entry
|
||||
%call = tail call i64 @random()
|
||||
%t0 = load i64, i64* @a
|
||||
%add = add nsw i64 %t0, %call
|
||||
store i64 %add, i64* @a
|
||||
br label %if.end
|
||||
|
||||
if.end: ; preds = %entry, %if.then
|
||||
tail call void @test3_c(i32 %i)
|
||||
tail call void @test3_b()
|
||||
%t6 = load i64, i64* @a
|
||||
%add79 = add nsw i64 %t6, 3
|
||||
store i64 %add79, i64* @a
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @test3_c(i32 %i) {
|
||||
entry:
|
||||
%cmp = icmp eq i32 %i, 5
|
||||
br i1 %cmp, label %if.end, label %if.then
|
||||
|
||||
if.then: ; preds = %entry
|
||||
%call = tail call i64 @random()
|
||||
%t0 = load i64, i64* @a
|
||||
%add = add nsw i64 %t0, %call
|
||||
store i64 %add, i64* @a
|
||||
br label %if.end
|
||||
|
||||
if.end: ; preds = %entry, %if.then
|
||||
tail call void @test3_d(i32 %i)
|
||||
%t6 = load i64, i64* @a
|
||||
%add85 = add nsw i64 %t6, 1
|
||||
store i64 %add85, i64* @a
|
||||
ret void
|
||||
}
|
||||
|
||||
declare i64 @random()
|
||||
|
||||
attributes #0 = { noinline }
|
||||
|
@ -64,14 +64,14 @@ return:
|
||||
; reducing an SCC in the inliner cannot accidentially leave stale function
|
||||
; analysis results due to failing to invalidate them for all the functions.
|
||||
|
||||
; The inliner visits this last function. It can't actually break any cycles
|
||||
; here, but because we visit this function we compute fresh analyses for it.
|
||||
; These analyses are then invalidated when we inline callee disrupting the
|
||||
; CFG, and it is important that they be freed.
|
||||
define void @test1_h() {
|
||||
; CHECK-LABEL: define void @test1_h()
|
||||
; We visit this function first in the inliner, and while we inline callee
|
||||
; perturbing the CFG, we don't inline anything else and the SCC structure
|
||||
; remains in tact.
|
||||
define void @test1_f() {
|
||||
; CHECK-LABEL: define void @test1_f()
|
||||
entry:
|
||||
call void @test1_g()
|
||||
; We force this edge to survive inlining.
|
||||
call void @test1_g() noinline
|
||||
; CHECK: call void @test1_g()
|
||||
|
||||
; Pull interesting CFG into this function.
|
||||
@ -105,14 +105,14 @@ entry:
|
||||
; CHECK: ret void
|
||||
}
|
||||
|
||||
; We visit this function first in the inliner, and while we inline callee
|
||||
; perturbing the CFG, we don't inline anything else and the SCC structure
|
||||
; remains in tact.
|
||||
define void @test1_f() {
|
||||
; CHECK-LABEL: define void @test1_f()
|
||||
; The inliner visits this last function. It can't actually break any cycles
|
||||
; here, but because we visit this function we compute fresh analyses for it.
|
||||
; These analyses are then invalidated when we inline callee disrupting the
|
||||
; CFG, and it is important that they be freed.
|
||||
define void @test1_h() {
|
||||
; CHECK-LABEL: define void @test1_h()
|
||||
entry:
|
||||
; We force this edge to survive inlining.
|
||||
call void @test1_g() noinline
|
||||
call void @test1_g()
|
||||
; CHECK: call void @test1_g()
|
||||
|
||||
; Pull interesting CFG into this function.
|
||||
|
@ -65,6 +65,25 @@ entry:
|
||||
; The 'test3_' prefixed functions test the scenario of not inlining preserving
|
||||
; dominators after splitting an SCC into two smaller SCCs.
|
||||
|
||||
; This function gets visited first and we end up inlining everything we
|
||||
; can into this routine. That splits test3_g into a separate SCC that is enqued
|
||||
; for later processing.
|
||||
define void @test3_f() {
|
||||
; CHECK-LABEL: define void @test3_f()
|
||||
entry:
|
||||
; Create the first edge in the SCC cycle.
|
||||
call void @test3_g()
|
||||
; CHECK-NOT: @test3_g()
|
||||
; CHECK: call void @test3_f()
|
||||
|
||||
; Pull interesting CFG into this function.
|
||||
call void @callee()
|
||||
; CHECK-NOT: call void @callee()
|
||||
|
||||
ret void
|
||||
; CHECK: ret void
|
||||
}
|
||||
|
||||
; This function ends up split into a separate SCC, which can cause its analyses
|
||||
; to become stale if the splitting doesn't properly invalidate things. Also, as
|
||||
; a consequence of being split out, test3_f is too large to inline by the time
|
||||
@ -83,22 +102,3 @@ entry:
|
||||
ret void
|
||||
; CHECK: ret void
|
||||
}
|
||||
|
||||
; The second function gets visited first and we end up inlining everything we
|
||||
; can into this routine. That splits test3_g into a separate SCC that is enqued
|
||||
; for later processing.
|
||||
define void @test3_f() {
|
||||
; CHECK-LABEL: define void @test3_f()
|
||||
entry:
|
||||
; Create the first edge in the SCC cycle.
|
||||
call void @test3_g()
|
||||
; CHECK-NOT: @test3_g()
|
||||
; CHECK: call void @test3_f()
|
||||
|
||||
; Pull interesting CFG into this function.
|
||||
call void @callee()
|
||||
; CHECK-NOT: call void @callee()
|
||||
|
||||
ret void
|
||||
; CHECK: ret void
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ target triple = "nvptx64-nvidia-cuda"
|
||||
@__omp_offloading_801_3022563__Z6maini1v_l17_exec_mode = weak constant i8 0
|
||||
@llvm.compiler.used = appending global [1 x i8*] [i8* @__omp_offloading_801_3022563__Z6maini1v_l17_exec_mode], section "llvm.metadata"
|
||||
|
||||
; CHECK: remark: declare_target_codegen_globalization.cpp:10:1: Found thread data sharing on the GPU. Expect degraded performance due to data globalization.
|
||||
; CHECK: remark: declare_target_codegen_globalization.cpp:17:1: Found thread data sharing on the GPU. Expect degraded performance due to data globalization.
|
||||
; CHECK: remark: declare_target_codegen_globalization.cpp:10:1: Found thread data sharing on the GPU. Expect degraded performance due to data globalization.
|
||||
|
||||
; Function Attrs: norecurse nounwind
|
||||
define weak void @__omp_offloading_801_3022563__Z6maini1v_l17(i32* nonnull align 4 dereferenceable(4) %a) local_unnamed_addr #0 !dbg !10 {
|
||||
|
@ -141,7 +141,6 @@ define internal void @merge_some..omp_par(i32* noalias nocapture readnone %0, i3
|
||||
; #pragma omp cancel parallel
|
||||
; }
|
||||
; }
|
||||
;
|
||||
; #pragma omp parallel
|
||||
; {
|
||||
; if (cancel2) {
|
||||
@ -206,7 +205,7 @@ declare i32 @__kmpc_cancel(%struct.ident_t*, i32, i32) local_unnamed_addr
|
||||
; CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* [[GLOB1]])
|
||||
; CHECK-NEXT: br label [[OMP_PARALLEL:%.*]]
|
||||
; CHECK: omp_parallel:
|
||||
; CHECK-NEXT: call void (%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) @__kmpc_fork_call(%struct.ident_t* [[GLOB1]], i32 1, void (i32*, i32*, ...)* bitcast (void (i32*, i32*, i32*)* @merge_all..omp_par.3 to void (i32*, i32*, ...)*), i32* [[TMP2]])
|
||||
; CHECK-NEXT: call void (%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) @__kmpc_fork_call(%struct.ident_t* [[GLOB1]], i32 1, void (i32*, i32*, ...)* bitcast (void (i32*, i32*, i32*)* @merge_all..omp_par.2 to void (i32*, i32*, ...)*), i32* [[TMP2]])
|
||||
; CHECK-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]]
|
||||
; CHECK: omp.par.outlined.exit:
|
||||
; CHECK-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]]
|
||||
@ -216,7 +215,7 @@ declare i32 @__kmpc_cancel(%struct.ident_t*, i32, i32) local_unnamed_addr
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
;
|
||||
; CHECK-LABEL: define {{[^@]+}}@merge_all..omp_par.3
|
||||
; CHECK-LABEL: define {{[^@]+}}@merge_all..omp_par.2
|
||||
; CHECK-SAME: (i32* noalias [[TID_ADDR:%.*]], i32* noalias [[ZERO_ADDR:%.*]], i32* [[TMP0:%.*]]) [[ATTR0:#.*]] {
|
||||
; CHECK-NEXT: omp.par.entry:
|
||||
; CHECK-NEXT: [[TID_ADDR_LOCAL:%.*]] = alloca i32, align 4
|
||||
@ -285,7 +284,7 @@ declare i32 @__kmpc_cancel(%struct.ident_t*, i32, i32) local_unnamed_addr
|
||||
; CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* [[GLOB1]])
|
||||
; CHECK-NEXT: br label [[OMP_PARALLEL:%.*]]
|
||||
; CHECK: omp_parallel:
|
||||
; CHECK-NEXT: call void (%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) @__kmpc_fork_call(%struct.ident_t* [[GLOB1]], i32 1, void (i32*, i32*, ...)* bitcast (void (i32*, i32*, i32*)* @merge_some..omp_par.2 to void (i32*, i32*, ...)*), i32* [[TMP2]])
|
||||
; CHECK-NEXT: call void (%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) @__kmpc_fork_call(%struct.ident_t* [[GLOB1]], i32 1, void (i32*, i32*, ...)* bitcast (void (i32*, i32*, i32*)* @merge_some..omp_par.5 to void (i32*, i32*, ...)*), i32* [[TMP2]])
|
||||
; CHECK-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]]
|
||||
; CHECK: omp.par.outlined.exit:
|
||||
; CHECK-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]]
|
||||
@ -295,7 +294,7 @@ declare i32 @__kmpc_cancel(%struct.ident_t*, i32, i32) local_unnamed_addr
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
;
|
||||
; CHECK-LABEL: define {{[^@]+}}@merge_some..omp_par.2
|
||||
; CHECK-LABEL: define {{[^@]+}}@merge_some..omp_par.5
|
||||
; CHECK-SAME: (i32* noalias [[TID_ADDR:%.*]], i32* noalias [[ZERO_ADDR:%.*]], i32* [[TMP0:%.*]]) [[ATTR0]] {
|
||||
; CHECK-NEXT: omp.par.entry:
|
||||
; CHECK-NEXT: [[TID_ADDR_LOCAL:%.*]] = alloca i32, align 4
|
||||
@ -349,7 +348,7 @@ declare i32 @__kmpc_cancel(%struct.ident_t*, i32, i32) local_unnamed_addr
|
||||
; CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* [[GLOB1]])
|
||||
; CHECK-NEXT: br label [[OMP_PARALLEL:%.*]]
|
||||
; CHECK: omp_parallel:
|
||||
; CHECK-NEXT: call void (%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) @__kmpc_fork_call(%struct.ident_t* [[GLOB1]], i32 2, void (i32*, i32*, ...)* bitcast (void (i32*, i32*, i32*, i32*)* @merge_cancellable_regions..omp_par.1 to void (i32*, i32*, ...)*), i32* [[TMP4]], i32* [[TMP5]])
|
||||
; CHECK-NEXT: call void (%struct.ident_t*, i32, void (i32*, i32*, ...)*, ...) @__kmpc_fork_call(%struct.ident_t* [[GLOB1]], i32 2, void (i32*, i32*, ...)* bitcast (void (i32*, i32*, i32*, i32*)* @merge_cancellable_regions..omp_par.6 to void (i32*, i32*, ...)*), i32* [[TMP4]], i32* [[TMP5]])
|
||||
; CHECK-NEXT: br label [[OMP_PAR_OUTLINED_EXIT:%.*]]
|
||||
; CHECK: omp.par.outlined.exit:
|
||||
; CHECK-NEXT: br label [[OMP_PAR_EXIT_SPLIT:%.*]]
|
||||
@ -359,7 +358,7 @@ declare i32 @__kmpc_cancel(%struct.ident_t*, i32, i32) local_unnamed_addr
|
||||
; CHECK-NEXT: ret void
|
||||
;
|
||||
;
|
||||
; CHECK-LABEL: define {{[^@]+}}@merge_cancellable_regions..omp_par.1
|
||||
; CHECK-LABEL: define {{[^@]+}}@merge_cancellable_regions..omp_par.6
|
||||
; CHECK-SAME: (i32* noalias [[TID_ADDR:%.*]], i32* noalias [[ZERO_ADDR:%.*]], i32* [[TMP0:%.*]], i32* [[TMP1:%.*]]) [[ATTR0]] {
|
||||
; CHECK-NEXT: omp.par.entry:
|
||||
; CHECK-NEXT: [[TID_ADDR_LOCAL:%.*]] = alloca i32, align 4
|
||||
|
@ -219,36 +219,36 @@ public:
|
||||
// |/
|
||||
// x
|
||||
//
|
||||
"define void @f() {\n"
|
||||
"define void @x() {\n"
|
||||
"entry:\n"
|
||||
" ret void\n"
|
||||
"}\n"
|
||||
"define void @h3() {\n"
|
||||
"entry:\n"
|
||||
" call void @g()\n"
|
||||
" call void @h1()\n"
|
||||
" ret void\n"
|
||||
"}\n"
|
||||
"define void @g() {\n"
|
||||
"entry:\n"
|
||||
" call void @g()\n"
|
||||
" call void @x()\n"
|
||||
" ret void\n"
|
||||
"}\n"
|
||||
"define void @h1() {\n"
|
||||
"entry:\n"
|
||||
" call void @h2()\n"
|
||||
" ret void\n"
|
||||
"}\n"
|
||||
"define void @h2() {\n"
|
||||
"entry:\n"
|
||||
" call void @h3()\n"
|
||||
" call void @x()\n"
|
||||
" ret void\n"
|
||||
"}\n"
|
||||
"define void @h3() {\n"
|
||||
"define void @h1() {\n"
|
||||
"entry:\n"
|
||||
" call void @h1()\n"
|
||||
" call void @h2()\n"
|
||||
" ret void\n"
|
||||
"}\n"
|
||||
"define void @x() {\n"
|
||||
"define void @g() {\n"
|
||||
"entry:\n"
|
||||
" call void @g()\n"
|
||||
" call void @x()\n"
|
||||
" ret void\n"
|
||||
"}\n"
|
||||
"define void @f() {\n"
|
||||
"entry:\n"
|
||||
" call void @g()\n"
|
||||
" call void @h1()\n"
|
||||
" ret void\n"
|
||||
"}\n")) {
|
||||
FAM.registerPass([&] { return TargetLibraryAnalysis(); });
|
||||
|
@ -337,22 +337,6 @@ TEST(LazyCallGraphTest, BasicGraphFormation) {
|
||||
EXPECT_FALSE(D.isDescendantOf(D));
|
||||
EXPECT_EQ(&D, &*CG.postorder_ref_scc_begin());
|
||||
|
||||
LazyCallGraph::RefSCC &C = *J++;
|
||||
ASSERT_EQ(1, C.size());
|
||||
for (LazyCallGraph::Node &N : *C.begin())
|
||||
Nodes.push_back(std::string(N.getFunction().getName()));
|
||||
llvm::sort(Nodes);
|
||||
EXPECT_EQ(3u, Nodes.size());
|
||||
EXPECT_EQ("c1", Nodes[0]);
|
||||
EXPECT_EQ("c2", Nodes[1]);
|
||||
EXPECT_EQ("c3", Nodes[2]);
|
||||
Nodes.clear();
|
||||
EXPECT_TRUE(C.isParentOf(D));
|
||||
EXPECT_FALSE(C.isChildOf(D));
|
||||
EXPECT_TRUE(C.isAncestorOf(D));
|
||||
EXPECT_FALSE(C.isDescendantOf(D));
|
||||
EXPECT_EQ(&C, &*std::next(CG.postorder_ref_scc_begin()));
|
||||
|
||||
LazyCallGraph::RefSCC &B = *J++;
|
||||
ASSERT_EQ(1, B.size());
|
||||
for (LazyCallGraph::Node &N : *B.begin())
|
||||
@ -367,9 +351,25 @@ TEST(LazyCallGraphTest, BasicGraphFormation) {
|
||||
EXPECT_FALSE(B.isChildOf(D));
|
||||
EXPECT_TRUE(B.isAncestorOf(D));
|
||||
EXPECT_FALSE(B.isDescendantOf(D));
|
||||
EXPECT_EQ(&B, &*std::next(CG.postorder_ref_scc_begin()));
|
||||
|
||||
LazyCallGraph::RefSCC &C = *J++;
|
||||
ASSERT_EQ(1, C.size());
|
||||
for (LazyCallGraph::Node &N : *C.begin())
|
||||
Nodes.push_back(std::string(N.getFunction().getName()));
|
||||
llvm::sort(Nodes);
|
||||
EXPECT_EQ(3u, Nodes.size());
|
||||
EXPECT_EQ("c1", Nodes[0]);
|
||||
EXPECT_EQ("c2", Nodes[1]);
|
||||
EXPECT_EQ("c3", Nodes[2]);
|
||||
Nodes.clear();
|
||||
EXPECT_FALSE(B.isAncestorOf(C));
|
||||
EXPECT_FALSE(C.isAncestorOf(B));
|
||||
EXPECT_EQ(&B, &*std::next(CG.postorder_ref_scc_begin(), 2));
|
||||
EXPECT_TRUE(C.isParentOf(D));
|
||||
EXPECT_FALSE(C.isChildOf(D));
|
||||
EXPECT_TRUE(C.isAncestorOf(D));
|
||||
EXPECT_FALSE(C.isDescendantOf(D));
|
||||
EXPECT_EQ(&C, &*std::next(CG.postorder_ref_scc_begin(), 2));
|
||||
|
||||
LazyCallGraph::RefSCC &A = *J++;
|
||||
ASSERT_EQ(1, A.size());
|
||||
@ -1206,10 +1206,10 @@ TEST(LazyCallGraphTest, InlineAndDeleteFunction) {
|
||||
ASSERT_NE(I, E);
|
||||
EXPECT_EQ(&NewDRC, &*I) << "Actual RefSCC: " << *I;
|
||||
ASSERT_NE(++I, E);
|
||||
EXPECT_EQ(&CRC, &*I) << "Actual RefSCC: " << *I;
|
||||
ASSERT_NE(++I, E);
|
||||
EXPECT_EQ(&BRC, &*I) << "Actual RefSCC: " << *I;
|
||||
ASSERT_NE(++I, E);
|
||||
EXPECT_EQ(&CRC, &*I) << "Actual RefSCC: " << *I;
|
||||
ASSERT_NE(++I, E);
|
||||
EXPECT_EQ(&ARC, &*I) << "Actual RefSCC: " << *I;
|
||||
EXPECT_EQ(++I, E);
|
||||
}
|
||||
@ -1997,8 +1997,8 @@ TEST(LazyCallGraphTest, HandleBlockAddress2) {
|
||||
|
||||
CG.buildRefSCCs();
|
||||
auto I = CG.postorder_ref_scc_begin();
|
||||
LazyCallGraph::RefSCC &GRC = *I++;
|
||||
LazyCallGraph::RefSCC &FRC = *I++;
|
||||
LazyCallGraph::RefSCC &GRC = *I++;
|
||||
EXPECT_EQ(CG.postorder_ref_scc_end(), I);
|
||||
|
||||
LazyCallGraph::Node &F = *CG.lookup(lookupFunction(*M, "f"));
|
||||
|
Loading…
Reference in New Issue
Block a user