[LazyCallGraph] Build SCCs of the reference graph in order
authorFangrui Song <i@maskray.me>
Mon, 2 Nov 2020 21:22:42 +0000 (13:22 -0800)
committerFangrui Song <i@maskray.me>
Mon, 2 Nov 2020 21:22:42 +0000 (13:22 -0800)
```
// 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

13 files changed:
llvm/lib/Analysis/LazyCallGraph.cpp
llvm/test/Analysis/LazyCallGraph/alias.ll
llvm/test/Analysis/LazyCallGraph/basic.ll
llvm/test/Other/scc-pass-printer.ll
llvm/test/Transforms/Attributor/IPConstantProp/PR26044.ll
llvm/test/Transforms/Inline/cgscc-cycle-debug.ll
llvm/test/Transforms/Inline/cgscc-cycle.ll
llvm/test/Transforms/Inline/cgscc-incremental-invalidate.ll
llvm/test/Transforms/Inline/cgscc-invalidate.ll
llvm/test/Transforms/OpenMP/globalization_remarks.ll
llvm/test/Transforms/OpenMP/parallel_region_merging.ll
llvm/unittests/Analysis/CGSCCPassManagerTest.cpp
llvm/unittests/Analysis/LazyCallGraphTest.cpp

index e00ceff..2652989 100644 (file)
@@ -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) {
index 7c9ca51..054c4d9 100644 (file)
@@ -8,13 +8,13 @@ target triple = "x86_64-grtev4-linux-gnu"
 ; CHECK:        Edges in function: bar
 ; CHECK:        Edges in function: baz
 
-; CHECK:        RefSCC with 1 call SCCs:
-; CHECK-NEXT:     SCC with 1 functions:
-; CHECK-NEXT:       bar
-
 ; CHECK:       RefSCC with 1 call SCCs:
 ; CHECK-NEXT:    SCC with 1 functions:
 ; CHECK-NEXT:      foo
+; CHECK-EMPTY:
+; CHECK:       RefSCC with 1 call SCCs:
+; CHECK-NEXT:    SCC with 1 functions:
+; CHECK-NEXT:      bar
 
 ; CHECK-NOT:       baz
 
index 0d05a7a..eaaecc5 100644 (file)
@@ -256,39 +256,20 @@ entry:
 ; Verify the SCCs formed.
 ;
 ; CHECK-LABEL: RefSCC with 1 call SCCs:
-; CHECK-NEXT:    SCC with 3 functions:
-; 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:
-; CHECK-NEXT:      test3_bb1
 ; CHECK-NEXT:    SCC with 1 functions:
-; CHECK-NEXT:      test3_ba1
-;
-; CHECK-LABEL: RefSCC with 3 call SCCs:
-; CHECK-NEXT:    SCC with 2 functions:
-; CHECK-NEXT:      test3_ac2
-; CHECK-NEXT:      test3_ac1
-; 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-NEXT:      f
 ;
 ; CHECK-LABEL: RefSCC with 1 call SCCs:
 ; CHECK-NEXT:    SCC with 1 functions:
-; CHECK-NEXT:      f7
+; CHECK-NEXT:      f1
 ;
 ; CHECK-LABEL: RefSCC with 1 call SCCs:
 ; CHECK-NEXT:    SCC with 1 functions:
-; CHECK-NEXT:      f6
+; CHECK-NEXT:      f2
 ;
 ; CHECK-LABEL: RefSCC with 1 call SCCs:
 ; CHECK-NEXT:    SCC with 1 functions:
-; CHECK-NEXT:      f5
+; CHECK-NEXT:      f3
 ;
 ; CHECK-LABEL: RefSCC with 1 call SCCs:
 ; CHECK-NEXT:    SCC with 1 functions:
@@ -296,27 +277,27 @@ entry:
 ;
 ; CHECK-LABEL: RefSCC with 1 call SCCs:
 ; CHECK-NEXT:    SCC with 1 functions:
-; CHECK-NEXT:      f3
+; CHECK-NEXT:      f5
 ;
 ; CHECK-LABEL: RefSCC with 1 call SCCs:
 ; CHECK-NEXT:    SCC with 1 functions:
-; CHECK-NEXT:      f2
+; CHECK-NEXT:      f6
 ;
 ; CHECK-LABEL: RefSCC with 1 call SCCs:
 ; CHECK-NEXT:    SCC with 1 functions:
-; CHECK-NEXT:      f1
+; CHECK-NEXT:      f7
 ;
 ; CHECK-LABEL: RefSCC with 1 call SCCs:
 ; CHECK-NEXT:    SCC with 1 functions:
-; CHECK-NEXT:      test2
+; CHECK-NEXT:      f8
 ;
 ; CHECK-LABEL: RefSCC with 1 call SCCs:
 ; CHECK-NEXT:    SCC with 1 functions:
-; CHECK-NEXT:      f10
+; CHECK-NEXT:      f9
 ;
 ; CHECK-LABEL: RefSCC with 1 call SCCs:
 ; CHECK-NEXT:    SCC with 1 functions:
-; CHECK-NEXT:      f12
+; CHECK-NEXT:      f10
 ;
 ; CHECK-LABEL: RefSCC with 1 call SCCs:
 ; CHECK-NEXT:    SCC with 1 functions:
@@ -324,11 +305,11 @@ entry:
 ;
 ; CHECK-LABEL: RefSCC with 1 call SCCs:
 ; CHECK-NEXT:    SCC with 1 functions:
-; CHECK-NEXT:      f9
+; CHECK-NEXT:      f12
 ;
 ; CHECK-LABEL: RefSCC with 1 call SCCs:
 ; CHECK-NEXT:    SCC with 1 functions:
-; CHECK-NEXT:      f8
+; CHECK-NEXT:      test0
 ;
 ; CHECK-LABEL: RefSCC with 1 call SCCs:
 ; CHECK-NEXT:    SCC with 1 functions:
@@ -336,8 +317,27 @@ entry:
 ;
 ; CHECK-LABEL: RefSCC with 1 call SCCs:
 ; CHECK-NEXT:    SCC with 1 functions:
-; CHECK-NEXT:      f
+; 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-LABEL: RefSCC with 2 call SCCs:
 ; CHECK-NEXT:    SCC with 1 functions:
-; CHECK-NEXT:      test0
+; CHECK-NEXT:      test3_bb1
+; CHECK-NEXT:    SCC with 1 functions:
+; CHECK-NEXT:      test3_ba1
+;
+; CHECK-LABEL: RefSCC with 3 call SCCs:
+; CHECK-NEXT:    SCC with 2 functions:
+; 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_aa1
+; CHECK-NEXT:      test3_aa2
index 9f0625d..9d6016b 100644 (file)
@@ -1,36 +1,50 @@
 ; 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()
-; INL: define 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 @tester()
-; INL-NEXT:  call void @foo()
-; INL: IR Dump After
+; 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()
+
+; 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: IR Dump After {{Function Integration/Inlining|InlinerPass .*scc: .bar, foo}}
+; INL-MOD-LABEL:*** IR Dump After InlinerPass *** (scc: (foo, bar))
 ; INL-MOD-NEXT: ModuleID =
 ; INL-MOD-NEXT: source_filename =
 ; INL-MOD: define void @tester()
 ; INL-MOD-NEXT:  call void @foo()
 ; INL-MOD: define void @foo()
-; INL-MOD-NEXT:   call void @bar()
+; 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-NEXT:   call void @foo()
+; INL-MOD-LABEL:*** IR Dump After InlinerPass *** (scc: (tester))
 ; INL-MOD-NEXT: ModuleID =
 ; INL-MOD-NEXT: source_filename =
 ; INL-MOD: define void @tester()
index ed4e499..f068180 100644 (file)
@@ -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
index 435d540..21a5ae5 100644 (file)
@@ -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)]
@@ -42,4 +42,4 @@ define void @test1_c(i32 %num) #0 {
   ret void
 }
 
-attributes #0 = { noinline nounwind optnone }
\ No newline at end of file
+attributes #0 = { noinline nounwind optnone }
index bc3bdc9..fafa945 100644 (file)
@@ -153,28 +153,39 @@ bb2:
 @a = global i64 0
 @b = global i64 0
 
-define void @test3_c(i32 %i) {
+; Check test3_c is inlined into test3_a once and only once.
+; CHECK-LABEL: @test3_a(
+; CHECK: tail call void @test3_b()
+; CHECK-NEXT: tail call void @test3_d(i32 5)
+; CHECK-NEXT: %[[LD1:.*]] = load i64, i64* @a
+; CHECK-NEXT: %[[ADD1:.*]] = add nsw i64 %[[LD1]], 1
+; CHECK-NEXT: store i64 %[[ADD1]], i64* @a
+; CHECK-NEXT: %[[LD2:.*]] = load i64, i64* @b
+; CHECK-NEXT: %[[ADD2:.*]] = add nsw i64 %[[LD2]], 5
+; CHECK-NEXT: store i64 %[[ADD2]], i64* @b
+; CHECK-NEXT: ret void
+
+; Function Attrs: noinline
+define void @test3_a() #0 {
 entry:
-  %cmp = icmp eq i32 %i, 5
-  br i1 %cmp, label %if.end, label %if.then
+  tail call void @test3_b()
+  tail call void @test3_c(i32 5)
+  %t0 = load i64, i64* @b
+  %add = add nsw i64 %t0, 5
+  store i64 %add, i64* @b
+  ret void
+}
 
-if.then:                                          ; preds = %entry
-  %call = tail call i64 @random()
+; Function Attrs: noinline
+define void @test3_b() #0 {
+entry:
+  tail call void @test3_a()
   %t0 = load i64, i64* @a
-  %add = add nsw i64 %t0, %call
+  %add = add nsw i64 %t0, 2
   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
@@ -196,37 +207,26 @@ if.end:                                           ; preds = %entry, %if.then
   ret void
 }
 
-; Function Attrs: noinline
-define void @test3_b() #0 {
+define void @test3_c(i32 %i) {
 entry:
-  tail call void @test3_a()
+  %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, 2
+  %add = add nsw i64 %t0, %call
   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()
-; CHECK-NEXT: tail call void @test3_d(i32 5)
-; CHECK-NEXT: %[[LD1:.*]] = load i64, i64* @a
-; CHECK-NEXT: %[[ADD1:.*]] = add nsw i64 %[[LD1]], 1
-; CHECK-NEXT: store i64 %[[ADD1]], i64* @a
-; CHECK-NEXT: %[[LD2:.*]] = load i64, i64* @b
-; CHECK-NEXT: %[[ADD2:.*]] = add nsw i64 %[[LD2]], 5
-; CHECK-NEXT: store i64 %[[ADD2]], i64* @b
-; CHECK-NEXT: ret void
+  br label %if.end
 
-; Function Attrs: noinline
-define void @test3_a() #0 {
-entry:
-  tail call void @test3_b()
-  tail call void @test3_c(i32 5)
-  %t0 = load i64, i64* @b
-  %add = add nsw i64 %t0, 5
-  store i64 %add, i64* @b
+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 }
index 311de67..448c213 100644 (file)
@@ -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.
index 69d84f6..725a28e 100644 (file)
@@ -65,15 +65,15 @@ entry:
 ; The 'test3_' prefixed functions test the scenario of not inlining preserving
 ; dominators after splitting an SCC into two smaller SCCs.
 
-; 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
-; we get here.
-define void @test3_g() {
-; CHECK-LABEL: define void @test3_g()
+; 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 second edge in the SCC cycle.
-  call void @test3_f()
+  ; 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.
@@ -84,15 +84,15 @@ entry:
 ; 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()
+; 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
+; we get here.
+define void @test3_g() {
+; CHECK-LABEL: define void @test3_g()
 entry:
-  ; Create the first edge in the SCC cycle.
-  call void @test3_g()
-; CHECK-NOT: @test3_g()
+  ; Create the second edge in the SCC cycle.
+  call void @test3_f()
 ; CHECK: call void @test3_f()
 
   ; Pull interesting CFG into this function.
index 77d3773..ea0ed82 100644 (file)
@@ -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 {
index 7086299..188f21d 100644 (file)
@@ -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
index 1b42ba2..9193a2d 100644 (file)
@@ -219,15 +219,18 @@ 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"
+            "define void @h2() {\n"
             "entry:\n"
-            "  call void @g()\n"
+            "  call void @h3()\n"
             "  call void @x()\n"
             "  ret void\n"
             "}\n"
@@ -236,20 +239,17 @@ public:
             "  call void @h2()\n"
             "  ret void\n"
             "}\n"
-            "define void @h2() {\n"
+            "define void @g() {\n"
             "entry:\n"
-            "  call void @h3()\n"
+            "  call void @g()\n"
             "  call void @x()\n"
             "  ret void\n"
             "}\n"
-            "define void @h3() {\n"
+            "define void @f() {\n"
             "entry:\n"
+            "  call void @g()\n"
             "  call void @h1()\n"
             "  ret void\n"
-            "}\n"
-            "define void @x() {\n"
-            "entry:\n"
-            "  ret void\n"
             "}\n")) {
     FAM.registerPass([&] { return TargetLibraryAnalysis(); });
     MAM.registerPass([&] { return LazyCallGraphAnalysis(); });
index 04c48c0..c2c1cd2 100644 (file)
@@ -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"));