Skip to content

Commit d160bc8

Browse files
committed
Add tests, clarify comments
1 parent d7127b6 commit d160bc8

File tree

3 files changed

+113
-3
lines changed

3 files changed

+113
-3
lines changed

llvm/lib/Transforms/IPO/FunctionAttrs.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,12 +2472,12 @@ PreservedAnalyses NoRecurseLTOInferencePass::run(Module &M,
24722472

24732473
for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs()) {
24742474
// Skip any RefSCC that is part of a call cycle. A RefSCC containing more
2475-
// than one SCC indicates a recursive relationship, which could involve
2476-
// direct or indirect calls.
2475+
// than one SCC indicates a recursive relationship involving indirect calls.
24772476
if (RC.size() > 1)
24782477
continue;
24792478

2480-
// A single-SCC RefSCC could still be a self-loop.
2479+
// RefSCC contains a single-SCC. SCC size > 1 indicates mutually recursive
2480+
// functions. Ex: foo1 -> foo2 -> foo3 -> foo1.
24812481
LazyCallGraph::SCC &S = *RC.begin();
24822482
if (S.size() > 1)
24832483
continue;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals all --version 5
2+
; RUN: opt < %s -passes=norecurse-lto-inference -S | FileCheck %s
3+
4+
; This test includes a call graph which has a recursive function(foo2) which
5+
; calls a non-recursive internal function (foo3) satisfying the norecurse
6+
; attribute criteria.
7+
8+
9+
define internal void @foo3() {
10+
; CHECK: Function Attrs: norecurse
11+
; CHECK-LABEL: define internal void @foo3(
12+
; CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
13+
; CHECK-NEXT: ret void
14+
;
15+
ret void
16+
}
17+
18+
define internal i32 @foo2(i32 %accum, i32 %n) {
19+
; CHECK-LABEL: define internal i32 @foo2(
20+
; CHECK-SAME: i32 [[ACCUM:%.*]], i32 [[N:%.*]]) {
21+
; CHECK-NEXT: [[ENTRY:.*]]:
22+
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[N]], 0
23+
; CHECK-NEXT: br i1 [[CMP]], label %[[EXIT:.*]], label %[[RECURSE:.*]]
24+
; CHECK: [[RECURSE]]:
25+
; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[N]], 1
26+
; CHECK-NEXT: [[MUL:%.*]] = mul i32 [[ACCUM]], [[SUB]]
27+
; CHECK-NEXT: [[CALL:%.*]] = call i32 @foo2(i32 [[MUL]], i32 [[SUB]])
28+
; CHECK-NEXT: call void @foo3()
29+
; CHECK-NEXT: br label %[[EXIT]]
30+
; CHECK: [[EXIT]]:
31+
; CHECK-NEXT: [[RES:%.*]] = phi i32 [ [[ACCUM]], %[[ENTRY]] ], [ [[CALL]], %[[RECURSE]] ]
32+
; CHECK-NEXT: ret i32 [[RES]]
33+
;
34+
entry:
35+
%cmp = icmp eq i32 %n, 0
36+
br i1 %cmp, label %exit, label %recurse
37+
38+
recurse:
39+
%sub = sub i32 %n, 1
40+
%mul = mul i32 %accum, %sub
41+
%call = call i32 @foo2(i32 %mul, i32 %sub)
42+
call void @foo3()
43+
br label %exit
44+
45+
exit:
46+
%res = phi i32 [ %accum, %entry ], [ %call, %recurse ]
47+
ret i32 %res
48+
}
49+
50+
define internal i32 @foo1() {
51+
; CHECK-LABEL: define internal i32 @foo1() {
52+
; CHECK-NEXT: [[RES:%.*]] = call i32 @foo2(i32 1, i32 5)
53+
; CHECK-NEXT: ret i32 [[RES]]
54+
;
55+
%res = call i32 @foo2(i32 1, i32 5)
56+
ret i32 %res
57+
}
58+
59+
define dso_local i32 @main() {
60+
; CHECK-LABEL: define dso_local i32 @main() {
61+
; CHECK-NEXT: [[RES:%.*]] = call i32 @foo1()
62+
; CHECK-NEXT: ret i32 [[RES]]
63+
;
64+
%res = call i32 @foo1()
65+
ret i32 %res
66+
}
67+
;.
68+
; CHECK: attributes #[[ATTR0]] = { norecurse }
69+
;.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals all --version 5
2+
; RUN: opt -passes=norecurse-lto-inference -S %s | FileCheck %s
3+
4+
; This is a negative test which results in RefSCC with size > 1.
5+
; RefSCC : [(f2), (f1)]
6+
; --- SCC A (f1) --- size() = 1
7+
define internal void @f1() {
8+
; CHECK-LABEL: define internal void @f1() {
9+
; CHECK-NEXT: call void @f2()
10+
; CHECK-NEXT: ret void
11+
;
12+
call void @f2()
13+
ret void
14+
}
15+
16+
; --- SCC B (f2) --- size() = 1
17+
; f2 indirectly calls f1 using locally allocated function pointer
18+
define internal void @f2() {
19+
; CHECK-LABEL: define internal void @f2() {
20+
; CHECK-NEXT: [[FP:%.*]] = alloca ptr, align 8
21+
; CHECK-NEXT: store ptr @f1, ptr [[FP]], align 8
22+
; CHECK-NEXT: [[TMP:%.*]] = load ptr, ptr [[FP]], align 8
23+
; CHECK-NEXT: call void [[TMP]]()
24+
; CHECK-NEXT: ret void
25+
;
26+
%fp = alloca void ()*
27+
store void ()* @f1, void ()** %fp
28+
%tmp = load void ()*, void ()** %fp
29+
call void %tmp()
30+
ret void
31+
}
32+
33+
define i32 @main() {
34+
; CHECK-LABEL: define i32 @main() {
35+
; CHECK-NEXT: call void @f1()
36+
; CHECK-NEXT: ret i32 0
37+
;
38+
call void @f1()
39+
ret i32 0
40+
}
41+

0 commit comments

Comments
 (0)