;; Test that correct clones are generated and reached when we need to ;; re-merge clone nodes before function assignment. ;; ;; The code is similar to that of basic.ll, but with a second allocation. ; RUN: opt -passes=memprof-context-disambiguation -supports-hot-cold-new \ ;; Disable merge iteration for now as it causes spurious diffs due to different ;; iteration order (but the same ultimate hinting of the contexts). ; RUN: -memprof-merge-iteration=false \ ; RUN: -memprof-verify-ccg -memprof-dump-ccg %s -S 2>&1 | FileCheck %s \ ; RUN: --check-prefix=IR --check-prefix=DUMP ;; Make sure the option to disable merging causes the expected regression. ; RUN: opt -passes=memprof-context-disambiguation -supports-hot-cold-new \ ; RUN: -memprof-merge-clones=false %s -S 2>&1 | FileCheck %s --check-prefix=NOMERGE ;; main should incorrectly call the same clone of foo. ; NOMERGE: define {{.*}} @main ; NOMERGE-NEXT: entry: ; NOMERGE-NEXT: call {{.*}} @_Z3foov.memprof.1() ; NOMERGE-NEXT: call {{.*}} @_Z3foov.memprof.1() target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" define i32 @main() { entry: ;; Ultimately calls bar and allocates notcold memory from first call to new ;; and cold memory from second call to new. %call = call noundef ptr @_Z3foov(), !callsite !0 ;; Ultimately calls bar and allocates cold memory from first call to new ;; and notcold memory from second call to new. %call1 = call noundef ptr @_Z3foov(), !callsite !1 ret i32 0 } define internal ptr @_Z3barv() { entry: ;; notcold when called from first call to foo from main, cold when called from second. %call = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #0, !memprof !2, !callsite !7 ;; cold when called from first call to foo from main, notcold when called from second. %call2 = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #0, !memprof !13, !callsite !18 ret ptr null } declare ptr @_Znam(i64) define internal ptr @_Z3bazv() { entry: %call = call noundef ptr @_Z3barv(), !callsite !8 ret ptr null } ; Function Attrs: noinline define internal ptr @_Z3foov() { entry: %call = call noundef ptr @_Z3bazv(), !callsite !9 ret ptr null } attributes #0 = { builtin } !0 = !{i64 8632435727821051414} !1 = !{i64 -3421689549917153178} !2 = !{!3, !5} !3 = !{!4, !"notcold"} !4 = !{i64 9086428284934609951, i64 -5964873800580613432, i64 2732490490862098848, i64 8632435727821051414} !5 = !{!6, !"cold"} !6 = !{i64 9086428284934609951, i64 -5964873800580613432, i64 2732490490862098848, i64 -3421689549917153178} !7 = !{i64 9086428284934609951} !8 = !{i64 -5964873800580613432} !9 = !{i64 2732490490862098848} !13 = !{!14, !16} !14 = !{!15, !"cold"} !15 = !{i64 123, i64 -5964873800580613432, i64 2732490490862098848, i64 8632435727821051414} !16 = !{!17, !"notcold"} !17 = !{i64 123, i64 -5964873800580613432, i64 2732490490862098848, i64 -3421689549917153178} !18 = !{i64 123} ;; After cloning, each callsite in main calls different clones of foo with ;; different allocaton types, and ditto all the way through the leaf ;; allocation callsites. The single allocation-type clones are shared between ;; the two callsites in main. This would lead to incorrect assignment of ;; the leaf allocations to function clones as is, since we have lost the ;; information that each callsite in main ultimately reaches two allocation ;; callsites with *different* allocation types. ; DUMP: CCG after cloning: ; DUMP: Callsite Context Graph: ; DUMP: Node [[BAR1ALLOC1:0x[a-f0-9]+]] ; DUMP: %call = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #0 (clone 0) ; DUMP: AllocTypes: NotCold ; DUMP: ContextIds: 1 ; DUMP: CalleeEdges: ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[BAR1ALLOC1]] to Caller: [[BAZ1:0x[a-f0-9]+]] AllocTypes: NotCold ContextIds: 1 ; DUMP: Clones: [[BAR2ALLOC1:0x[a-f0-9]+]] ; DUMP: Node [[BAZ1]] ; DUMP: %call = call noundef ptr @_Z3barv() (clone 0) ; DUMP: AllocTypes: NotCold ; DUMP: ContextIds: 1 4 ; DUMP: CalleeEdges: ; DUMP: Edge from Callee [[BAR1ALLOC1]] to Caller: [[BAZ1]] AllocTypes: NotCold ContextIds: 1 ; DUMP: Edge from Callee [[BAR1ALLOC2:0x[a-f0-9]+]] to Caller: [[BAZ1]] AllocTypes: NotCold ContextIds: 4 ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[BAZ1]] to Caller: [[FOO1:0x[a-f0-9]+]] AllocTypes: NotCold ContextIds: 1 4 ; DUMP: Clones: [[BAZ2:0x[a-f0-9]+]] ; DUMP: Node [[FOO1]] ; DUMP: %call = call noundef ptr @_Z3bazv() (clone 0) ; DUMP: AllocTypes: NotCold ; DUMP: ContextIds: 1 4 ; DUMP: CalleeEdges: ; DUMP: Edge from Callee [[BAZ1]] to Caller: [[FOO1]] AllocTypes: NotCold ContextIds: 1 4 ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[FOO1]] to Caller: [[MAIN1:0x[a-f0-9]+]] AllocTypes: NotCold ContextIds: 1 ; DUMP: Edge from Callee [[FOO1]] to Caller: [[MAIN2:0x[a-f0-9]+]] AllocTypes: NotCold ContextIds: 4 ; DUMP: Clones: [[FOO2:0x[a-f0-9]+]] ; DUMP: Node [[MAIN1]] ; DUMP: %call = call noundef ptr @_Z3foov() (clone 0) ; DUMP: AllocTypes: NotColdCold ; DUMP: ContextIds: 1 3 ; DUMP: CalleeEdges: ; DUMP: Edge from Callee [[FOO1]] to Caller: [[MAIN1]] AllocTypes: NotCold ContextIds: 1 ; DUMP: Edge from Callee [[FOO2:0x[a-f0-9]+]] to Caller: [[MAIN1]] AllocTypes: Cold ContextIds: 3 ; DUMP: CallerEdges: ; DUMP: Node [[MAIN2]] ; DUMP: %call1 = call noundef ptr @_Z3foov() (clone 0) ; DUMP: AllocTypes: NotColdCold ; DUMP: ContextIds: 2 4 ; DUMP: CalleeEdges: ; DUMP: Edge from Callee [[FOO1]] to Caller: [[MAIN2]] AllocTypes: NotCold ContextIds: 4 ; DUMP: Edge from Callee [[FOO2]] to Caller: [[MAIN2]] AllocTypes: Cold ContextIds: 2 ; DUMP: CallerEdges: ; DUMP: Node [[BAR1ALLOC2]] ; DUMP: %call2 = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #0 (clone 0) ; DUMP: AllocTypes: NotCold ; DUMP: ContextIds: 4 ; DUMP: CalleeEdges: ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[BAR1ALLOC2]] to Caller: [[BAZ1]] AllocTypes: NotCold ContextIds: 4 ; DUMP: Clones: [[BAR2ALLOC2:0x[a-f0-9]+]] ; DUMP: Node [[FOO2]] ; DUMP: %call = call noundef ptr @_Z3bazv() (clone 0) ; DUMP: AllocTypes: Cold ; DUMP: ContextIds: 2 3 ; DUMP: CalleeEdges: ; DUMP: Edge from Callee [[BAZ2]] to Caller: [[FOO2]] AllocTypes: Cold ContextIds: 2 3 ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[FOO2]] to Caller: [[MAIN2]] AllocTypes: Cold ContextIds: 2 ; DUMP: Edge from Callee [[FOO2]] to Caller: [[MAIN1]] AllocTypes: Cold ContextIds: 3 ; DUMP: Clone of [[FOO1]] ; DUMP: Node [[BAZ2]] ; DUMP: %call = call noundef ptr @_Z3barv() (clone 0) ; DUMP: AllocTypes: Cold ; DUMP: ContextIds: 2 3 ; DUMP: CalleeEdges: ; DUMP: Edge from Callee [[BAR2ALLOC1]] to Caller: [[BAZ2]] AllocTypes: Cold ContextIds: 2 ; DUMP: Edge from Callee [[BAR2ALLOC2]] to Caller: [[BAZ2]] AllocTypes: Cold ContextIds: 3 ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[BAZ2]] to Caller: [[FOO2]] AllocTypes: Cold ContextIds: 2 3 ; DUMP: Clone of [[BAZ1]] ; DUMP: Node [[BAR2ALLOC1]] ; DUMP: %call = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #0 (clone 0) ; DUMP: AllocTypes: Cold ; DUMP: ContextIds: 2 ; DUMP: CalleeEdges: ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[BAR2ALLOC1]] to Caller: [[BAZ2]] AllocTypes: Cold ContextIds: 2 ; DUMP: Clone of [[BAR1ALLOC1]] ; DUMP: Node [[BAR2ALLOC2]] ; DUMP: %call2 = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #0 (clone 0) ; DUMP: AllocTypes: Cold ; DUMP: ContextIds: 3 ; DUMP: CalleeEdges: ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[BAR2ALLOC2]] to Caller: [[BAZ2]] AllocTypes: Cold ContextIds: 3 ; DUMP: Clone of [[BAR1ALLOC2]] ;; After merging, each callsite in main calls a different single clone of foo ;; with both cold and not cold allocation types, but ultimately reaches two ;; single allocation type allocation callsite clones of the correct ;; combination. The graph after assigning function clones is the same, but ;; with function calls updated to the new function clones. ; DUMP: CCG after merging: ; DUMP: Callsite Context Graph: ; DUMP: Node [[BAR1ALLOC1]] ; DUMP: %call = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #0 (clone 0) ; DUMP: AllocTypes: NotCold ; DUMP: ContextIds: 1 ; DUMP: CalleeEdges: ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[BAR1ALLOC1]] to Caller: [[BAZ2]] AllocTypes: NotCold ContextIds: 1 ; DUMP: Clones: [[BAR2ALLOC1]] ; DUMP: Node [[MAIN1]] ; DUMP: %call = call noundef ptr @_Z3foov() (clone 0) ; DUMP: AllocTypes: NotColdCold ; DUMP: ContextIds: 1 3 ; DUMP: CalleeEdges: ; DUMP: Edge from Callee [[FOO3:0x[a-f0-9]+]] to Caller: [[MAIN1]] AllocTypes: NotColdCold ContextIds: 1 3 ; DUMP: CallerEdges: ; DUMP: Node [[MAIN2]] ; DUMP: %call1 = call noundef ptr @_Z3foov() (clone 0) ; DUMP: AllocTypes: NotColdCold ; DUMP: ContextIds: 2 4 ; DUMP: CalleeEdges: ; DUMP: Edge from Callee [[FOO2]] to Caller: [[MAIN2]] AllocTypes: NotColdCold ContextIds: 2 4 ; DUMP: CallerEdges: ; DUMP: Node [[BAR1ALLOC2]] ; DUMP: %call2 = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #0 (clone 0) ; DUMP: AllocTypes: NotCold ; DUMP: ContextIds: 4 ; DUMP: CalleeEdges: ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[BAR1ALLOC2]] to Caller: [[BAZ3:0x[a-f0-9]+]] AllocTypes: NotCold ContextIds: 4 ; DUMP: Clones: [[BAR2ALLOC2]] ; DUMP: Node [[FOO2]] ; DUMP: %call = call noundef ptr @_Z3bazv() (clone 0) ; DUMP: AllocTypes: NotColdCold ; DUMP: ContextIds: 2 4 ; DUMP: CalleeEdges: ; DUMP: Edge from Callee [[BAZ3]] to Caller: [[FOO2]] AllocTypes: NotColdCold ContextIds: 2 4 ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[FOO2]] to Caller: [[MAIN2]] AllocTypes: NotColdCold ContextIds: 2 4 ; DUMP: Clone of [[FOO1]] ; DUMP: Node [[BAZ2]] ; DUMP: %call = call noundef ptr @_Z3barv() (clone 0) ; DUMP: AllocTypes: NotColdCold ; DUMP: ContextIds: 1 3 ; DUMP: CalleeEdges: ; DUMP: Edge from Callee [[BAR2ALLOC2]] to Caller: [[BAZ2]] AllocTypes: Cold ContextIds: 3 ; DUMP: Edge from Callee [[BAR1ALLOC1]] to Caller: [[BAZ2]] AllocTypes: NotCold ContextIds: 1 ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[BAZ2]] to Caller: [[FOO3]] AllocTypes: NotColdCold ContextIds: 1 3 ; DUMP: Clone of [[BAZ1]] ; DUMP: Node [[BAR2ALLOC1]] ; DUMP: %call = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #0 (clone 0) ; DUMP: AllocTypes: Cold ; DUMP: ContextIds: 2 ; DUMP: CalleeEdges: ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[BAR2ALLOC1]] to Caller: [[BAZ3]] AllocTypes: Cold ContextIds: 2 ; DUMP: Clone of [[BAR1ALLOC1]] ; DUMP: Node [[BAR2ALLOC2]] ; DUMP: %call2 = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #0 (clone 0) ; DUMP: AllocTypes: Cold ; DUMP: ContextIds: 3 ; DUMP: CalleeEdges: ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[BAR2ALLOC2]] to Caller: [[BAZ2]] AllocTypes: Cold ContextIds: 3 ; DUMP: Clone of [[BAR1ALLOC2]] ; DUMP: Node [[FOO3]] ; DUMP: %call = call noundef ptr @_Z3bazv() (clone 0) ; DUMP: AllocTypes: NotColdCold ; DUMP: ContextIds: 1 3 ; DUMP: CalleeEdges: ; DUMP: Edge from Callee [[BAZ2]] to Caller: [[FOO3]] AllocTypes: NotColdCold ContextIds: 1 3 ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[FOO3]] to Caller: [[MAIN1]] AllocTypes: NotColdCold ContextIds: 1 3 ; DUMP: Clone of [[FOO1]] ; DUMP: Node [[BAZ3]] ; DUMP: %call = call noundef ptr @_Z3barv() (clone 0) ; DUMP: AllocTypes: NotColdCold ; DUMP: ContextIds: 2 4 ; DUMP: CalleeEdges: ; DUMP: Edge from Callee [[BAR2ALLOC1]] to Caller: [[BAZ3]] AllocTypes: Cold ContextIds: 2 ; DUMP: Edge from Callee [[BAR1ALLOC2]] to Caller: [[BAZ3]] AllocTypes: NotCold ContextIds: 4 ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[BAZ3]] to Caller: [[FOO2]] AllocTypes: NotColdCold ContextIds: 2 4 ; DUMP: Clone of [[BAZ1]] ; DUMP: CCG after assigning function clones: ; DUMP: Callsite Context Graph: ; DUMP: Node [[BAR1ALLOC1]] ; DUMP: %call = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #0 (clone 0) ; DUMP: AllocTypes: NotCold ; DUMP: ContextIds: 1 ; DUMP: CalleeEdges: ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[BAR1ALLOC1]] to Caller: [[BAZ2]] AllocTypes: NotCold ContextIds: 1 ; DUMP: Clones: [[BAR2ALLOC1]] ; DUMP: Node [[MAIN1]] ; DUMP: %call = call noundef ptr @_Z3foov.memprof.1() (clone 0) ; DUMP: AllocTypes: NotColdCold ; DUMP: ContextIds: 1 3 ; DUMP: CalleeEdges: ; DUMP: Edge from Callee [[FOO3]] to Caller: [[MAIN1]] AllocTypes: NotColdCold ContextIds: 1 3 ; DUMP: CallerEdges: ; DUMP: Node [[MAIN2]] ; DUMP: %call1 = call noundef ptr @_Z3foov() (clone 0) ; DUMP: AllocTypes: NotColdCold ; DUMP: ContextIds: 2 4 ; DUMP: CalleeEdges: ; DUMP: Edge from Callee [[FOO2]] to Caller: [[MAIN2]] AllocTypes: NotColdCold ContextIds: 2 4 ; DUMP: CallerEdges: ; DUMP: Node [[BAR1ALLOC2]] ; DUMP: %call2 = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #1 (clone 1) ; DUMP: AllocTypes: NotCold ; DUMP: ContextIds: 4 ; DUMP: CalleeEdges: ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[BAR1ALLOC2]] to Caller: [[BAZ3]] AllocTypes: NotCold ContextIds: 4 ; DUMP: Clones: [[BAR2ALLOC2]] ; DUMP: Node [[FOO2]] ; DUMP: %call = call noundef ptr @_Z3bazv.memprof.1() (clone 0) ; DUMP: AllocTypes: NotColdCold ; DUMP: ContextIds: 2 4 ; DUMP: CalleeEdges: ; DUMP: Edge from Callee [[BAZ3]] to Caller: [[FOO2]] AllocTypes: NotColdCold ContextIds: 2 4 ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[FOO2]] to Caller: [[MAIN2]] AllocTypes: NotColdCold ContextIds: 2 4 ; DUMP: Clone of [[FOO1]] ; DUMP: Node [[BAZ2]] ; DUMP: %call = call noundef ptr @_Z3barv() (clone 0) ; DUMP: AllocTypes: NotColdCold ; DUMP: ContextIds: 1 3 ; DUMP: CalleeEdges: ; DUMP: Edge from Callee [[BAR2ALLOC2]] to Caller: [[BAZ2]] AllocTypes: Cold ContextIds: 3 ; DUMP: Edge from Callee [[BAR1ALLOC1]] to Caller: [[BAZ2]] AllocTypes: NotCold ContextIds: 1 ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[BAZ2]] to Caller: [[FOO3]] AllocTypes: NotColdCold ContextIds: 1 3 ; DUMP: Clone of [[BAZ1]] ; DUMP: Node [[BAR2ALLOC1]] ; DUMP: %call = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #0 (clone 1) ; DUMP: AllocTypes: Cold ; DUMP: ContextIds: 2 ; DUMP: CalleeEdges: ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[BAR2ALLOC1]] to Caller: [[BAZ3]] AllocTypes: Cold ContextIds: 2 ; DUMP: Clone of [[BAR1ALLOC1]] ; DUMP: Node [[BAR2ALLOC2]] ; DUMP: %call2 = call noalias noundef nonnull ptr @_Znam(i64 noundef 10) #1 (clone 0) ; DUMP: AllocTypes: Cold ; DUMP: ContextIds: 3 ; DUMP: CalleeEdges: ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[BAR2ALLOC2]] to Caller: [[BAZ2]] AllocTypes: Cold ContextIds: 3 ; DUMP: Clone of [[BAR1ALLOC2]] ; DUMP: Node [[FOO3]] ; DUMP: %call = call noundef ptr @_Z3bazv() (clone 1) ; DUMP: AllocTypes: NotColdCold ; DUMP: ContextIds: 1 3 ; DUMP: CalleeEdges: ; DUMP: Edge from Callee [[BAZ2]] to Caller: [[FOO3]] AllocTypes: NotColdCold ContextIds: 1 3 ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[FOO3]] to Caller: [[MAIN1]] AllocTypes: NotColdCold ContextIds: 1 3 ; DUMP: Clone of [[FOO1]] ; DUMP: Node [[BAZ3]] ; DUMP: %call = call noundef ptr @_Z3barv.memprof.1() (clone 1) ; DUMP: AllocTypes: NotColdCold ; DUMP: ContextIds: 2 4 ; DUMP: CalleeEdges: ; DUMP: Edge from Callee [[BAR2ALLOC1]] to Caller: [[BAZ3]] AllocTypes: Cold ContextIds: 2 ; DUMP: Edge from Callee [[BAR1ALLOC2]] to Caller: [[BAZ3]] AllocTypes: NotCold ContextIds: 4 ; DUMP: CallerEdges: ; DUMP: Edge from Callee [[BAZ3]] to Caller: [[FOO2]] AllocTypes: NotColdCold ContextIds: 2 4 ; DUMP: Clone of [[BAZ1]] ; IR: define {{.*}} @main ;; The first call to foo should now call foo.memprof.1 that ultimately ;; calls bar with the first allocation hinted notcold and the second cold ;; (via call chain foo.memprof.1 -> baz -> bar). ; IR: call {{.*}} @_Z3foov.memprof.1() ;; The second call to foo still calls the original foo, but ultimately ;; reaches a clone of bar with the first allocation hinted cold and the ;; second notcold. ; IR: call {{.*}} @_Z3foov() ; IR: define internal {{.*}} @_Z3barv() ; IR: call {{.*}} @_Znam(i64 noundef 10) #[[NOTCOLD:[0-9]+]] ; IR: call {{.*}} @_Znam(i64 noundef 10) #[[COLD:[0-9]+]] ; IR: define internal {{.*}} @_Z3bazv() ; IR: call {{.*}} @_Z3barv() ; IR: define internal {{.*}} @_Z3foov() ; IR: call {{.*}} @_Z3bazv.memprof.1() ; IR: define internal {{.*}} @_Z3barv.memprof.1() ; IR: call {{.*}} @_Znam(i64 noundef 10) #[[COLD]] ; IR: call {{.*}} @_Znam(i64 noundef 10) #[[NOTCOLD]] ; IR: define internal {{.*}} @_Z3bazv.memprof.1() ; IR: call {{.*}} @_Z3barv.memprof.1() ; IR: define internal {{.*}} @_Z3foov.memprof.1() ; IR: call {{.*}} @_Z3bazv() ; IR: attributes #[[NOTCOLD]] = { builtin "memprof"="notcold" } ; IR: attributes #[[COLD]] = { builtin "memprof"="cold" }