xref: /llvm-project/llvm/test/Transforms/MemProfContextDisambiguation/overlapping-contexts.ll (revision a332cfc986e431de11cdbf632b5769878e0d826b)
1;; This test ensures that the logic which assigns calls to stack nodes
2;; correctly handles cloning of a callsite for a trimmed cold context
3;; that partially overlaps with a longer context for a different allocation.
4
5;; The profile data and call stacks were all manually added, but the code
6;; would be structured something like the following (fairly contrived to
7;; result in the type of control flow needed to test):
8
9;; void A(bool b) {
10;;   if (b)
11;;     // cold: stack ids 10, 12, 13, 15 (trimmed ids 19, 20)
12;;     // not cold: stack ids 10, 12, 13, 14 (trimmed id 21)
13;;     new char[10]; // stack id 10
14;;   else
15;;     // not cold: stack ids 11, 12, 13, 15, 16, 17 (trimmed id 22)
16;;     // cold: stack ids 11, 12, 13, 15, 16, 18 (trimmed id 23)
17;;     new char[10]; // stack id 11
18;; }
19;;
20;; void X(bool b) {
21;;   A(b); // stack ids 12
22;; }
23;;
24;; void B(bool b) {
25;;   X(b); // stack id 13
26;; }
27;;
28;; void D() {
29;;   B(true); // stack id 14
30;; }
31;;
32;; void C(bool b) {
33;;   B(b); // stack id 15
34;; }
35;;
36;; void E(bool b) {
37;;   C(b); // stack id 16
38;; }
39;;
40;; void F() {
41;;   E(false); // stack id 17
42;; }
43;;
44;; void G() {
45;;   E(false); // stack id 18
46;; }
47;;
48;; void M() {
49;;   C(true); // stack id 19
50;; }
51;;
52;; int main() {
53;;   D(); // stack id 20 (leads to not cold allocation)
54;;   M(); // stack id 21 (leads to cold allocation)
55;;   F(); // stack id 22 (leads to not cold allocation)
56;;   G(); // stack id 23 (leads to cold allocation)
57;; }
58
59;; -stats requires asserts
60; REQUIRES: asserts
61
62; RUN: opt -passes=memprof-context-disambiguation -supports-hot-cold-new \
63; RUN:	-memprof-verify-ccg -memprof-verify-nodes \
64; RUN:  -stats -pass-remarks=memprof-context-disambiguation \
65; RUN:	%s -S 2>&1 | FileCheck %s --check-prefix=IR \
66; RUN:  --check-prefix=STATS --check-prefix=REMARKS
67
68; REMARKS: created clone _Z1Ab.memprof.1
69; REMARKS: created clone _Z1Xb.memprof.1
70; REMARKS: created clone _Z1Bb.memprof.1
71; REMARKS: created clone _Z1Cb.memprof.1
72; REMARKS: created clone _Z1Eb.memprof.1
73; REMARKS: call in clone _Z1Gv assigned to call function clone _Z1Eb.memprof.1
74; REMARKS: call in clone _Z1Eb.memprof.1 assigned to call function clone _Z1Cb.memprof.1
75;; If we don't perform cloning for each allocation separately, we will miss
76;; cloning _Z1Cb for the trimmed cold allocation context leading to the
77;; allocation at stack id 10.
78; REMARKS: call in clone _Z1Cb.memprof.1 assigned to call function clone _Z1Bb.memprof.1
79; REMARKS: call in clone _Z1Fv assigned to call function clone _Z1Eb
80; REMARKS: call in clone _Z1Eb assigned to call function clone _Z1Cb
81; REMARKS: call in clone _Z1Cb assigned to call function clone _Z1Bb.memprof.1
82; REMARKS: call in clone _Z1Bb.memprof.1 assigned to call function clone _Z1Xb.memprof.1
83; REMARKS: call in clone _Z1Xb.memprof.1 assigned to call function clone _Z1Ab.memprof.1
84; REMARKS: call in clone _Z1Ab.memprof.1 marked with memprof allocation attribute cold
85; REMARKS: call in clone _Z1Bb.memprof.1 assigned to call function clone _Z1Xb
86; REMARKS: call in clone _Z1Dv assigned to call function clone _Z1Bb
87; REMARKS: call in clone _Z1Bb assigned to call function clone _Z1Xb
88; REMARKS: call in clone _Z1Xb assigned to call function clone _Z1Ab
89; REMARKS: call in clone _Z1Ab marked with memprof allocation attribute notcold
90; REMARKS: call in clone _Z1Ab.memprof.1 marked with memprof allocation attribute cold
91; REMARKS: call in clone _Z1Ab marked with memprof allocation attribute notcold
92
93
94target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
95target triple = "x86_64-unknown-linux-gnu"
96
97define dso_local void @_Z1Ab(i1 noundef zeroext %b) {
98entry:
99  br i1 %b, label %if.then, label %if.else
100
101if.then:
102  %call = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #7, !memprof !0, !callsite !10
103  br label %if.end
104
105if.else:
106  %call2 = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #7, !memprof !5, !callsite !11
107  br label %if.end
108
109if.end:
110  ret void
111}
112
113; Function Attrs: nobuiltin
114declare ptr @_Znam(i64) #0
115
116define dso_local void @_Z1Xb(i1 noundef zeroext %b) {
117entry:
118  tail call void @_Z1Ab(i1 noundef zeroext %b), !callsite !12
119  ret void
120}
121
122define dso_local void @_Z1Bb(i1 noundef zeroext %b) {
123entry:
124  tail call void @_Z1Xb(i1 noundef zeroext %b), !callsite !13
125  ret void
126}
127
128define dso_local void @_Z1Dv() {
129entry:
130  tail call void @_Z1Bb(i1 noundef zeroext true), !callsite !14
131  ret void
132}
133
134define dso_local void @_Z1Cb(i1 noundef zeroext %b) {
135entry:
136  tail call void @_Z1Bb(i1 noundef zeroext %b), !callsite !15
137  ret void
138}
139
140define dso_local void @_Z1Eb(i1 noundef zeroext %b) {
141entry:
142  tail call void @_Z1Cb(i1 noundef zeroext %b), !callsite !16
143  ret void
144}
145
146define dso_local void @_Z1Fv() {
147entry:
148  tail call void @_Z1Eb(i1 noundef zeroext false), !callsite !17
149  ret void
150}
151
152define dso_local void @_Z1Gv() {
153entry:
154  tail call void @_Z1Eb(i1 noundef zeroext false), !callsite !18
155  ret void
156}
157
158define dso_local void @_Z1Mv() {
159entry:
160  tail call void @_Z1Cb(i1 noundef zeroext true), !callsite !19
161  ret void
162}
163
164define dso_local noundef i32 @main() local_unnamed_addr {
165entry:
166  tail call void @_Z1Dv(), !callsite !20 ;; Not cold context
167  tail call void @_Z1Mv(), !callsite !21 ;; Cold context
168  tail call void @_Z1Fv(), !callsite !22 ;; Not cold context
169  tail call void @_Z1Gv(), !callsite !23 ;; Cold context
170  ret i32 0
171}
172
173attributes #0 = { nobuiltin }
174attributes #7 = { builtin }
175
176!0 = !{!1, !3}
177;; Cold (trimmed) context via call to _Z1Dv in main
178!1 = !{!2, !"cold"}
179!2 = !{i64 10, i64 12, i64 13, i64 15}
180;; Not cold (trimmed) context via call to _Z1Mv in main
181!3 = !{!4, !"notcold"}
182!4 = !{i64 10, i64 12, i64 13, i64 14}
183!5 = !{!6, !8}
184;; Not cold (trimmed) context via call to _Z1Fv in main
185!6 = !{!7, !"notcold"}
186!7 = !{i64 11, i64 12, i64 13, i64 15, i64 16, i64 17}
187;; Cold (trimmed) context via call to _Z1Gv in main
188!8 = !{!9, !"cold"}
189!9 = !{i64 11, i64 12, i64 13, i64 15, i64 16, i64 18}
190!10 = !{i64 10}
191!11 = !{i64 11}
192!12 = !{i64 12}
193!13 = !{i64 13}
194!14 = !{i64 14}
195!15 = !{i64 15}
196!16 = !{i64 16}
197!17 = !{i64 17}
198!18 = !{i64 18}
199!19 = !{i64 19}
200!20 = !{i64 20}
201!21 = !{i64 21}
202!22 = !{i64 22}
203!23 = !{i64 23}
204
205; IR: define {{.*}} @_Z1Cb(i1 noundef zeroext %b)
206; IR-NEXT: entry:
207; IR-NEXT:   call {{.*}} @_Z1Bb.memprof.1(i1 noundef zeroext %b)
208
209; IR: define {{.*}} @_Z1Ab.memprof.1(i1 noundef zeroext %b)
210; IR-NEXT: entry:
211; IR-NEXT:   br i1 %b, label %if.then, label %if.else
212; IR-EMPTY:
213; IR-NEXT: if.then:
214; IR-NEXT:   call {{.*}} @_Znam(i64 noundef 10) #[[COLD:[0-9]+]]
215; IR-NEXT:   br label %if.end
216; IR-EMPTY:
217; IR-NEXT: if.else:
218; IR-NEXT:   call {{.*}} @_Znam(i64 noundef 10) #[[COLD]]
219
220; IR: define {{.*}} @_Z1Xb.memprof.1(i1 noundef zeroext %b)
221; IR-NEXT: entry:
222; IR-NEXT:   call {{.*}} @_Z1Ab.memprof.1(i1 noundef zeroext %b)
223
224; IR: define {{.*}} @_Z1Bb.memprof.1(i1 noundef zeroext %b)
225; IR-NEXT: entry:
226; IR-NEXT:   call {{.*}} @_Z1Xb.memprof.1(i1 noundef zeroext %b)
227
228; IR: attributes #[[COLD]] = { builtin "memprof"="cold" }
229
230; STATS: 2 memprof-context-disambiguation - Number of cold static allocations (possibly cloned)
231; STATS: 2 memprof-context-disambiguation - Number of not cold static allocations (possibly cloned)
232; STATS: 5 memprof-context-disambiguation - Number of function clones created during whole program analysis
233