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