1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2; RUN: opt -passes=attributor -S < %s | FileCheck %s --check-prefixes=CHECK
3; RUN: opt -passes=attributor -attributor-print-call-graph -S -disable-output < %s | FileCheck %s --check-prefixes=DOT
4
5define dso_local void @func1() {
6; CHECK-LABEL: @func1(
7; CHECK-NEXT:    br label [[TMP2:%.*]]
8; CHECK:       1:
9; CHECK-NEXT:    unreachable
10; CHECK:       2:
11; CHECK-NEXT:    call void @func3()
12; CHECK-NEXT:    ret void
13;
14  %1 = icmp ne i32 0, 0
15  br i1 %1, label %2, label %3
16
172:                                                ; preds = %0
18  call void @func2()
19  br label %3
20
213:                                                ; preds = %2, %0
22  call void () @func3()
23  ret void
24}
25
26declare void @func3()
27declare void @func4()
28
29define dso_local void @func2() {
30; CHECK-LABEL: @func2(
31; CHECK-NEXT:    call void @func4()
32; CHECK-NEXT:    ret void
33;
34  call void @func4()
35  ret void
36}
37
38
39define void @func5(i32 %0) {
40; CHECK-LABEL: @func5(
41; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i32 [[TMP0:%.*]], 0
42; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], void ()* @func4, void ()* @func3
43; CHECK-NEXT:    call void [[TMP3]]()
44; CHECK-NEXT:    ret void
45;
46  %2 = icmp ne i32 %0, 0
47  %3 = select i1 %2, void ()* @func4, void ()* @func3
48  call void () %3()
49  ret void
50}
51
52define void @broker(void ()* %unknown) !callback !0 {
53; CHECK-LABEL: @broker(
54; CHECK-NEXT:    call void [[UNKNOWN:%.*]]()
55; CHECK-NEXT:    ret void
56;
57  call void %unknown()
58  ret void
59}
60
61define void @func6() {
62; CHECK-LABEL: @func6(
63; CHECK-NEXT:    call void @broker(void ()* nocapture nofree noundef @func3)
64; CHECK-NEXT:    ret void
65;
66  call void @broker(void ()* @func3)
67  ret void
68}
69
70define void @func7(void ()* %unknown) {
71; CHECK-LABEL: @func7(
72; CHECK-NEXT:    call void [[UNKNOWN:%.*]](), !callees !2
73; CHECK-NEXT:    ret void
74;
75  call void %unknown(), !callees !2
76  ret void
77}
78
79; Check there's no crash if something that isn't a function appears in !callees
80define void @undef_in_callees() {
81; CHECK-LABEL: @undef_in_callees(
82; CHECK-NEXT:  cond.end.i:
83; CHECK-NEXT:    call void undef(i8* undef, i32 undef, i8* undef), !callees !3
84; CHECK-NEXT:    ret void
85;
86cond.end.i:
87  call void undef(i8* undef, i32 undef, i8* undef), !callees !3
88  ret void
89}
90
91!0 = !{!1}
92!1 = !{i64 0, i1 false}
93!2 = !{void ()* @func3, void ()* @func4}
94!3 = distinct !{void (i8*, i32, i8*)* undef, void (i8*, i32, i8*)* null}
95
96; UTC_ARGS: --disable
97
98; DOT-DAG: Node[[FUNC1:0x[a-z0-9]+]] [shape=record,label="{func1}"];
99; DOT-DAG: Node[[FUNC2:0x[a-z0-9]+]] [shape=record,label="{func2}"];
100; DOT-DAG: Node[[FUNC3:0x[a-z0-9]+]] [shape=record,label="{func3}"];
101; DOT-DAG: Node[[FUNC4:0x[a-z0-9]+]] [shape=record,label="{func4}"];
102; DOT-DAG: Node[[FUNC5:0x[a-z0-9]+]] [shape=record,label="{func5}"];
103; DOT-DAG: Node[[FUNC6:0x[a-z0-9]+]] [shape=record,label="{func6}"];
104; DOT-DAG: Node[[FUNC7:0x[a-z0-9]+]] [shape=record,label="{func7}"];
105
106; DOT-DAG: Node[[BROKER:0x[a-z0-9]+]] [shape=record,label="{broker}"];
107
108; DOT-DAG: Node[[FUNC1]] -> Node[[FUNC3]];
109; DOT-DAG: Node[[FUNC2]] -> Node[[FUNC4]];
110; DOT-DAG: Node[[FUNC5]] -> Node[[FUNC3]];
111; DOT-DAG: Node[[FUNC5]] -> Node[[FUNC4]];
112
113; DOT-DAG: Node[[FUNC6]] -> Node[[BROKER]];
114
115; This one gets added because of the callback metadata.
116; DOT-DAG: Node[[FUNC6]] -> Node[[FUNC3]];
117
118; These ones are added because of the callees metadata.
119; DOT-DAG: Node[[FUNC7]] -> Node[[FUNC3]];
120; DOT-DAG: Node[[FUNC7]] -> Node[[FUNC4]];
121