blob: 8accc836456ed91096b19405fea226689e809973 [file] [log] [blame]
;; Check that duplicate spurious duplicate (identical) clones are simply
;; created as aliases to the first identical copy, rather than creating
;; multiple clones that call the same callee clones or have the same
;; allocation types. This currently happens in some cases due to additional
;; cloning performed during function assignment.
;;
;; The ThinLTO combined summary was manually modified as described there
;; to force multiple identical copies of various functions.
;; -stats requires asserts
; REQUIRES: asserts
; RUN: rm -rf %t && split-file %s %t && cd %t
; RUN: llvm-as src.ll -o src.o
; RUN: llvm-as src.o.thinlto.ll -o src.o.thinlto.bc
; RUN: opt -passes=memprof-context-disambiguation -stats \
; RUN: -memprof-import-summary=src.o.thinlto.bc \
; RUN: -pass-remarks=memprof-context-disambiguation \
; RUN: src.o -S 2>&1 | FileCheck %s
; CHECK: created clone bar.memprof.1
;; Duplicates of bar are created as declarations since bar is available_externally,
;; and the compiler does not well support available_externally aliases.
; CHECK: created clone decl bar.memprof.2
; CHECK: created clone decl bar.memprof.3
; CHECK: created clone _Z3foov.memprof.1
;; Duplicates of _Z3foov are created as aliases to the appropriate materialized
;; clone of _Z3foov.
; CHECK: created clone alias _Z3foov.memprof.2
; CHECK: created clone alias _Z3foov.memprof.3
;--- src.ll
source_filename = "memprof-distrib-alias.ll"
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"
@_Z8fooAliasv = alias ptr (...), ptr @_Z3foov
;; Original alias is unchanged.
; CHECK: @_Z8fooAliasv = alias ptr (...), ptr @_Z3foov{{$}}
;; We create an equivalent alias for the cloned def @_Z3foov.memprof.1.
; CHECK: @_Z8fooAliasv.memprof.1 = alias ptr (...), ptr @_Z3foov.memprof.1
;; We should also create aliases for the duplicate clones of _Z3foov
;; (_Z3foov.memprof.2 and _Z3foov.memprof.3) to the versions they are duplicates
;; of, and ditto for the associated @_Z8fooAliasv clones.
;;
;; _Z3foov.memprof.2 is a duplicate of original _Z3foov, and thus so is _Z8fooAliasv.memprof.2
; CHECK: @_Z3foov.memprof.2 = alias ptr (), ptr @_Z3foov{{$}}
; CHECK: @_Z8fooAliasv.memprof.2 = alias ptr (...), ptr @_Z3foov{{$}}
;; _Z3foov.memprof.3 is a duplicate of _Z3foov.memprof.1, and thus so is _Z8fooAliasv.memprof.3
; CHECK: @_Z3foov.memprof.3 = alias ptr (), ptr @_Z3foov.memprof.1
; CHECK: @_Z8fooAliasv.memprof.3 = alias ptr (...), ptr @_Z3foov.memprof.1
; CHECK-LABEL: define i32 @main()
define i32 @main() #0 {
entry:
;; The first call to bar does not allocate cold memory. It should call
;; the original function, which eventually calls the original allocation
;; decorated with a "notcold" attribute.
; CHECK: call {{.*}} @bar()
%call = call ptr @bar(), !callsite !0
;; The second call to bar allocates cold memory. It should call the cloned
;; function which eventually calls a cloned allocation decorated with a
;; "cold" attribute.
; CHECK: call {{.*}} @bar.memprof.1()
%call1 = call ptr @bar(), !callsite !1
ret i32 0
}
; CHECK-LABEL: define available_externally i32 @bar()
define available_externally i32 @bar() #0 {
entry:
; CHECK: call {{.*}} @_Z8fooAliasv()
%call = call ptr @_Z8fooAliasv(), !callsite !8
ret i32 0
}
declare ptr @_Znam(i64)
; CHECK-LABEL: define ptr @_Z3foov()
define ptr @_Z3foov() #0 {
entry:
; CHECK: call {{.*}} @_Znam(i64 0) #[[NOTCOLD:[0-9]+]]
%call = call ptr @_Znam(i64 0), !memprof !2, !callsite !7
ret ptr null
}
; We create actual clone for bar.memprof.1.
; CHECK: define available_externally i32 @bar.memprof.1()
; CHECK: call {{.*}} @_Z3foov.memprof.1()
;; bar.memprof.2 and bar.memprof.3 are duplicates (of original bar and
;; bar.memprof.1, respectively). However, they are available externally,
;; so rather than create an alias we simply create a declaration, since the
;; compiler does not fully support available_externally aliases.
; CHECK: declare i32 @bar.memprof.2
; CHECK: declare i32 @bar.memprof.3
; We create actual clone for foo.memprof.1.
; CHECK: define {{.*}} @_Z3foov.memprof.1()
; CHECK: call {{.*}} @_Znam(i64 0) #[[COLD:[0-9]+]]
; CHECK: attributes #[[NOTCOLD]] = { "memprof"="notcold" }
; CHECK: attributes #[[COLD]] = { "memprof"="cold" }
; CHECK: 4 memprof-context-disambiguation - Number of function clone duplicates detected during ThinLTO backend
; CHECK: 2 memprof-context-disambiguation - Number of function clones created during ThinLTO backend
attributes #0 = { noinline optnone }
!0 = !{i64 8632435727821051414}
!1 = !{i64 -3421689549917153178}
!2 = !{!3, !5}
!3 = !{!4, !"notcold"}
!4 = !{i64 9086428284934609951, i64 1234, i64 8632435727821051414}
!5 = !{!6, !"cold"}
!6 = !{i64 9086428284934609951, i64 1234, i64 -3421689549917153178}
!7 = !{i64 9086428284934609951}
!8 = !{i64 1234}
;--- src.o.thinlto.ll
; ModuleID = 'src.o.thinlto.ll'
source_filename = "src.o.thinlto.bc"
^0 = module: (path: "src.o", hash: (1720506022, 1575514144, 2506794664, 3599359797, 3160884478))
^1 = gv: (guid: 6583049656999245004, summaries: (alias: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 1, dsoLocal: 1, canAutoHide: 0, importType: definition), aliasee: ^2)))
;; Summary for _Z3foov, where the allocs part has been manually modified to add
;; two additional clones that are the same as the prior versions:
;; ... allocs: ((versions: (notcold, cold, notcold, cold), ...
^2 = gv: (guid: 9191153033785521275, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 1, dsoLocal: 1, canAutoHide: 0, importType: definition), insts: 2, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 1, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 0, mustBeUnreachable: 0), allocs: ((versions: (notcold, cold, notcold, cold), memProf: ((type: notcold, stackIds: (1234, 8632435727821051414)), (type: cold, stackIds: (1234, 15025054523792398438))))))))
^3 = gv: (guid: 15822663052811949562, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 1, dsoLocal: 1, canAutoHide: 0, importType: definition), insts: 3, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 1, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 0, mustBeUnreachable: 0), calls: ((callee: ^4)), callsites: ((callee: ^4, clones: (0), stackIds: (8632435727821051414)), (callee: ^4, clones: (1), stackIds: (15025054523792398438))))))
;; Summary for bar, where the callsites part has been manually modified to add
;; two additional clones that are the same as the prior clones:
;; ... callsites: ((callee: ^1, clones: (0, 1, 0, 1), ...
^4 = gv: (guid: 16434608426314478903, summaries: (function: (module: ^0, flags: (linkage: available_externally, visibility: default, notEligibleToImport: 0, live: 1, dsoLocal: 1, canAutoHide: 0, importType: definition), insts: 2, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 1, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 0, mustBeUnreachable: 0), calls: ((callee: ^1)), callsites: ((callee: ^1, clones: (0, 1, 0, 1), stackIds: (1234))))))
^6 = flags: 353
^7 = blockcount: 0