diff options
Diffstat (limited to 'llvm/test/Transforms/FunctionImport')
28 files changed, 1171 insertions, 0 deletions
diff --git a/llvm/test/Transforms/FunctionImport/Inputs/adjustable_threshold.ll b/llvm/test/Transforms/FunctionImport/Inputs/adjustable_threshold.ll new file mode 100644 index 00000000000..fd4644d264a --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/Inputs/adjustable_threshold.ll @@ -0,0 +1,37 @@ +define void @globalfunc1() { +entry: + call void @trampoline() + ret void +} +; Adds an artificial level in the call graph to reduce the importing threshold +define void @trampoline() { +entry: + call void @largefunction() + ret void +} + +define void @globalfunc2() { +entry: + call void @largefunction() + ret void +} + + +; Size is 5: if two layers below in the call graph the threshold will be 4, +; but if only one layer below the threshold will be 7. +define void @largefunction() { + entry: + call void @staticfunc2() + call void @staticfunc2() + call void @staticfunc2() + call void @staticfunc2() + call void @staticfunc2() + ret void +} + +define internal void @staticfunc2() { +entry: + ret void +} + + diff --git a/llvm/test/Transforms/FunctionImport/Inputs/comdat.ll b/llvm/test/Transforms/FunctionImport/Inputs/comdat.ll new file mode 100644 index 00000000000..1df6f25351e --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/Inputs/comdat.ll @@ -0,0 +1,10 @@ +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc19.0.24215" + +define void @main() { +entry: + call i8* @lwt_fun() + ret void +} + +declare i8* @lwt_fun() diff --git a/llvm/test/Transforms/FunctionImport/Inputs/funcimport.ll b/llvm/test/Transforms/FunctionImport/Inputs/funcimport.ll new file mode 100644 index 00000000000..201d4492671 --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/Inputs/funcimport.ll @@ -0,0 +1,165 @@ +@globalvar = global i32 1, align 4 +@staticvar = internal global i32 1, align 4 +@staticconstvar = internal unnamed_addr constant [2 x i32] [i32 10, i32 20], align 4 +@commonvar = common global i32 0, align 4 +@P = internal global void ()* null, align 8 + +@weakalias = weak alias void (...), bitcast (void ()* @globalfunc1 to void (...)*) +@analias = alias void (...), bitcast (void ()* @globalfunc2 to void (...)*) +@linkoncealias = alias void (...), bitcast (void ()* @linkoncefunc to void (...)*) + +define void @globalfunc1() #0 { +entry: + call void @funcwithpersonality() + call void (...) @variadic_va_start() + ret void +} + +define void @globalfunc2() #0 { +entry: + ret void +} + +define linkonce_odr void @linkoncefunc() #0 { +entry: + ret void +} + +define i32 @referencestatics(i32 %i) #0 { +entry: + %i.addr = alloca i32, align 4 + store i32 %i, i32* %i.addr, align 4 + %call = call i32 @staticfunc() + %0 = load i32, i32* @staticvar, align 4 + %add = add nsw i32 %call, %0 + %1 = load i32, i32* %i.addr, align 4 + %idxprom = sext i32 %1 to i64 + %arrayidx = getelementptr inbounds [2 x i32], [2 x i32]* @staticconstvar, i64 0, i64 %idxprom + %2 = load i32, i32* %arrayidx, align 4 + %add1 = add nsw i32 %add, %2 + ret i32 %add1 +} + +define i32 @referenceglobals(i32 %i) #0 { +entry: + %i.addr = alloca i32, align 4 + store i32 %i, i32* %i.addr, align 4 + call void @globalfunc1() + %0 = load i32, i32* @globalvar, align 4 + ret i32 %0 +} + +define i32 @referencecommon(i32 %i) #0 { +entry: + %i.addr = alloca i32, align 4 + store i32 %i, i32* %i.addr, align 4 + %0 = load i32, i32* @commonvar, align 4 + ret i32 %0 +} + +define void @setfuncptr() #0 { +entry: + store void ()* @staticfunc2, void ()** @P, align 8 + ret void +} + +define void @callfuncptr() #0 { +entry: + %0 = load void ()*, void ()** @P, align 8 + call void %0() + ret void +} + +@weakvar = weak global i32 1, align 4 +define weak void @weakfunc() #0 { +entry: + ret void +} + +define linkonce void @linkoncefunc2() #0 { +entry: + ret void +} + +define internal i32 @staticfunc() #0 { +entry: + ret i32 1 +} + +declare i32 @__gxx_personality_v0(...) + +; Add enough instructions to prevent import with inst limit of 5 +define internal void @funcwithpersonality() #2 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { +entry: + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + ret void +} + +define internal void @staticfunc2() #0 { +entry: + ret void +} + +define void @referencelargelinkonce() #0 { +entry: + call void @linkonceodr() + ret void +} + +; A large enough linkonce_odr function that should never be imported +define linkonce_odr void @linkonceodr() #0 { +entry: + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + call void @globalfunc2() + ret void +} + +; Variadic function without va_start can be imported because inliner +; can handle it. +define void @variadic_no_va_start(...) { + ret void +} + +; Variadic function with va_start should not be imported because inliner +; doesn't handle it. +define void @variadic_va_start(...) { + %ap = alloca i8*, align 8 + %ap.0 = bitcast i8** %ap to i8* + call void @llvm.va_start(i8* %ap.0) + ret void +} + +declare void @llvm.va_start(i8*) nounwind diff --git a/llvm/test/Transforms/FunctionImport/Inputs/funcimport_alias.ll b/llvm/test/Transforms/FunctionImport/Inputs/funcimport_alias.ll new file mode 100644 index 00000000000..f897aeda6ce --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/Inputs/funcimport_alias.ll @@ -0,0 +1,7 @@ +declare void @analias() + +define void @callanalias() #0 { +entry: + call void @analias() + ret void +} diff --git a/llvm/test/Transforms/FunctionImport/Inputs/funcimport_cutoff.ll b/llvm/test/Transforms/FunctionImport/Inputs/funcimport_cutoff.ll new file mode 100644 index 00000000000..4283215d74c --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/Inputs/funcimport_cutoff.ll @@ -0,0 +1,9 @@ +define void @foo() { +entry: + ret void +} + +define void @bar() { +entry: + ret void +} diff --git a/llvm/test/Transforms/FunctionImport/Inputs/funcimport_debug.ll b/llvm/test/Transforms/FunctionImport/Inputs/funcimport_debug.ll new file mode 100644 index 00000000000..f553af41896 --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/Inputs/funcimport_debug.ll @@ -0,0 +1,26 @@ +; ModuleID = 'funcimport_debug.o' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: nounwind uwtable +define void @func() #0 !dbg !4 { +entry: + ret void, !dbg !10 +} + +attributes #0 = { nounwind uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!7, !8} +!llvm.ident = !{!9} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.8.0 (trunk 255685) (llvm/trunk 255682)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "funcimport_debug.c", directory: ".") +!2 = !{} +!4 = distinct !DISubprogram(name: "func", scope: !1, file: !1, line: 1, type: !5, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: false, unit: !0, retainedNodes: !2) +!5 = !DISubroutineType(types: !6) +!6 = !{null} +!7 = !{i32 2, !"Dwarf Version", i32 4} +!8 = !{i32 2, !"Debug Info Version", i32 3} +!9 = !{!"clang version 3.8.0 (trunk 255685) (llvm/trunk 255682)"} +!10 = !DILocation(line: 2, column: 1, scope: !4) diff --git a/llvm/test/Transforms/FunctionImport/Inputs/funcimport_forcecold.ll b/llvm/test/Transforms/FunctionImport/Inputs/funcimport_forcecold.ll new file mode 100644 index 00000000000..a59d68c1e18 --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/Inputs/funcimport_forcecold.ll @@ -0,0 +1,4 @@ +define void @foo() { +entry: + ret void +} diff --git a/llvm/test/Transforms/FunctionImport/Inputs/funcimport_resolved1.ll b/llvm/test/Transforms/FunctionImport/Inputs/funcimport_resolved1.ll new file mode 100644 index 00000000000..2b2443c96af --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/Inputs/funcimport_resolved1.ll @@ -0,0 +1,34 @@ +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +define void @foo() { + call void @linkonceodrfunc() + call void @linkonceodrfunc2() + ret void +} + +define linkonce_odr void @linkonceodrfunc() { + call void @f() + call void @f() + call void @f() + call void @f() + call void @f() + call void @f() + call void @f() + ret void +} + +define linkonce_odr void @linkonceodrfunc2() { + call void @f() + call void @f() + call void @f() + call void @f() + call void @f() + call void @f() + call void @f() + ret void +} + +define internal void @f() { + ret void +} diff --git a/llvm/test/Transforms/FunctionImport/Inputs/funcimport_resolved2.ll b/llvm/test/Transforms/FunctionImport/Inputs/funcimport_resolved2.ll new file mode 100644 index 00000000000..278a7f4553f --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/Inputs/funcimport_resolved2.ll @@ -0,0 +1,6 @@ +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +define linkonce_odr void @linkonceodrfunc() { + ret void +} diff --git a/llvm/test/Transforms/FunctionImport/Inputs/funcimport_var2.ll b/llvm/test/Transforms/FunctionImport/Inputs/funcimport_var2.ll new file mode 100644 index 00000000000..95abe658e34 --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/Inputs/funcimport_var2.ll @@ -0,0 +1,10 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@link = internal global i32 0, align 4 + +; Function Attrs: norecurse nounwind readnone uwtable +define nonnull i32* @get_link() local_unnamed_addr { + ret i32* @link +} + diff --git a/llvm/test/Transforms/FunctionImport/Inputs/hotness_based_import.ll b/llvm/test/Transforms/FunctionImport/Inputs/hotness_based_import.ll new file mode 100644 index 00000000000..6951b65818d --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/Inputs/hotness_based_import.ll @@ -0,0 +1,81 @@ +; ModuleID = 'thinlto-function-summary-callgraph-profile-summary2.ll' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + + +define void @hot1() #1 { + ret void +} +define void @hot2() #1 !prof !20 { + call void @calledFromHot() + call void @calledFromHot() + ret void +} +define void @hot3() #1 !prof !20 { + call void @calledFromHot() + call void @calledFromHot() + call void @calledFromHot() + ret void +} +define void @cold() #1 !prof !0 { + ret void +} +define void @cold2() #1 !prof !0 { + call void @calledFromCold() + call void @calledFromCold() + ret void +} + +define void @none1() #1 { + ret void +} + +define void @none2() #1 { + call void @calledFromNone() + ret void +} +define void @none3() #1 { + call void @calledFromNone() + call void @calledFromNone() + ret void +} + +define void @calledFromCold() { + ret void +} + +define void @calledFromHot() !prof !20 { + call void @calledFromHot2() + ret void +} + +define void @calledFromHot2() !prof !20 { + call void @calledFromHot3() + ret void +} + +define void @calledFromNone() !prof !0 { + ret void +} + +declare void @calledFromHot3() + +!0 = !{!"function_entry_count", i64 1} +!20 = !{!"function_entry_count", i64 110} + +!llvm.module.flags = !{!1} + +!1 = !{i32 1, !"ProfileSummary", !2} +!2 = !{!3, !4, !5, !6, !7, !8, !9, !10} +!3 = !{!"ProfileFormat", !"InstrProf"} +!4 = !{!"TotalCount", i64 10000} +!5 = !{!"MaxCount", i64 10} +!6 = !{!"MaxInternalCount", i64 1} +!7 = !{!"MaxFunctionCount", i64 1000} +!8 = !{!"NumCounts", i64 3} +!9 = !{!"NumFunctions", i64 3} +!10 = !{!"DetailedSummary", !11} +!11 = !{!12, !13, !14} +!12 = !{i32 10000, i64 100, i32 1} +!13 = !{i32 999000, i64 100, i32 1} +!14 = !{i32 999999, i64 1, i32 2}
\ No newline at end of file diff --git a/llvm/test/Transforms/FunctionImport/Inputs/import_stats.ll b/llvm/test/Transforms/FunctionImport/Inputs/import_stats.ll new file mode 100644 index 00000000000..818fbf20d6f --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/Inputs/import_stats.ll @@ -0,0 +1,16 @@ +; ModuleID = 'import_stats2.ll' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@globalvar = global i32 1, align 4 + +define void @hot() { + store i32 0, i32* @globalvar, align 4 + ret void +} +define void @critical() { + ret void +} +define void @none() { + ret void +} diff --git a/llvm/test/Transforms/FunctionImport/Inputs/inlineasm.ll b/llvm/test/Transforms/FunctionImport/Inputs/inlineasm.ll new file mode 100644 index 00000000000..1ffc5db5f8b --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/Inputs/inlineasm.ll @@ -0,0 +1,11 @@ +@myvar = internal constant i8 1, align 1 +@llvm.used = appending global [1 x i8*] [i8* @myvar], section "llvm.metadata" + +define void @foo(i64* %v) #0 { +entry: + %v.addr = alloca i64*, align 8 + store i64* %v, i64** %v.addr, align 8 + %0 = load i64*, i64** %v.addr, align 8 + call void asm sideeffect "movzbl myvar(%rip), %eax\0A\09movq %rax, $0\0A\09", "=*m,~{eax},~{dirflag},~{fpsr},~{flags}"(i64* %0) #1 + ret void +} diff --git a/llvm/test/Transforms/FunctionImport/Inputs/not-prevailing.ll b/llvm/test/Transforms/FunctionImport/Inputs/not-prevailing.ll new file mode 100644 index 00000000000..ca17d7f377c --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/Inputs/not-prevailing.ll @@ -0,0 +1,6 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define weak i32 @foo() { + ret i32 0 +} diff --git a/llvm/test/Transforms/FunctionImport/adjustable_threshold.ll b/llvm/test/Transforms/FunctionImport/adjustable_threshold.ll new file mode 100644 index 00000000000..adb8b0dffb0 --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/adjustable_threshold.ll @@ -0,0 +1,31 @@ +; Do setup work for all below tests: generate bitcode and combined index +; RUN: opt -module-summary %s -o %t.bc +; RUN: opt -module-summary %p/Inputs/adjustable_threshold.ll -o %t2.bc +; RUN: llvm-lto -thinlto -o %t3 %t.bc %t2.bc + +; Test import with default progressive instruction factor +; RUN: opt -function-import -summary-file %t3.thinlto.bc %t.bc -import-instr-limit=10 -S | FileCheck %s --check-prefix=CHECK --check-prefix=INSTLIM-DEFAULT +; INSTLIM-DEFAULT: call void @staticfunc2.llvm. + +; Test import with a reduced progressive instruction factor +; RUN: opt -function-import -summary-file %t3.thinlto.bc %t.bc -import-instr-limit=10 -import-instr-evolution-factor=0.5 -S | FileCheck %s --check-prefix=CHECK --check-prefix=INSTLIM-PROGRESSIVE +; INSTLIM-PROGRESSIVE-NOT: call void @staticfunc + + + +declare void @globalfunc1() +declare void @globalfunc2() + +define void @entry() { +entry: +; Call site are processed in reversed order! + +; On the direct call, we reconsider @largefunction with a higher threshold and +; import it + call void @globalfunc2() +; When importing globalfunc1, the threshold was limited and @largefunction was +; not imported. + call void @globalfunc1() + ret void +} + diff --git a/llvm/test/Transforms/FunctionImport/comdat.ll b/llvm/test/Transforms/FunctionImport/comdat.ll new file mode 100644 index 00000000000..29e8cb538ab --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/comdat.ll @@ -0,0 +1,32 @@ +; Test to ensure that comdat is renamed consistently when comdat leader is +; promoted and renamed due to an import. Required by COFF. + +; REQUIRES: x86-registered-target + +; RUN: opt -thinlto-bc -o %t1.bc %s +; RUN: opt -thinlto-bc -o %t2.bc %S/Inputs/comdat.ll +; RUN: llvm-lto2 run -save-temps -o %t3 %t1.bc %t2.bc \ +; RUN: -r %t1.bc,lwt_fun,plx \ +; RUN: -r %t2.bc,main,plx \ +; RUN: -r %t2.bc,lwt_fun, +; RUN: llvm-dis -o - %t3.1.3.import.bc | FileCheck %s + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc19.0.24215" + +; CHECK: $lwt.llvm.[[HASH:[0-9]+]] = comdat any +$lwt = comdat any + +; CHECK: @lwt_aliasee = private unnamed_addr global {{.*}}, comdat($lwt.llvm.[[HASH]]) +@lwt_aliasee = private unnamed_addr global [1 x i8*] [i8* null], comdat($lwt) + +; CHECK: @lwt.llvm.[[HASH]] = hidden unnamed_addr alias +@lwt = internal unnamed_addr alias [1 x i8*], [1 x i8*]* @lwt_aliasee + +; Below function should get imported into other module, resulting in @lwt being +; promoted and renamed. +define i8* @lwt_fun() { + %1 = getelementptr inbounds [1 x i8*], [1 x i8*]* @lwt, i32 0, i32 0 + %2 = load i8*, i8** %1 + ret i8* %2 +} diff --git a/llvm/test/Transforms/FunctionImport/funcimport.ll b/llvm/test/Transforms/FunctionImport/funcimport.ll new file mode 100644 index 00000000000..0d09d94a0fc --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/funcimport.ll @@ -0,0 +1,162 @@ +; Do setup work for all below tests: generate bitcode and combined index +; RUN: opt -module-summary %s -o %t.bc +; RUN: opt -module-summary %p/Inputs/funcimport.ll -o %t2.bc +; RUN: llvm-lto -thinlto -print-summary-global-ids -o %t3 %t.bc %t2.bc 2>&1 | FileCheck %s --check-prefix=GUID + +; Do the import now +; RUN: opt -function-import -stats -print-imports -enable-import-metadata -summary-file %t3.thinlto.bc %t.bc -S 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=INSTLIMDEF +; Try again with new pass manager +; RUN: opt -passes='function-import' -stats -print-imports -enable-import-metadata -summary-file %t3.thinlto.bc %t.bc -S 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=INSTLIMDEF +; RUN: opt -passes='function-import' -debug-only=function-import -enable-import-metadata -summary-file %t3.thinlto.bc %t.bc -S 2>&1 | FileCheck %s --check-prefix=DUMP +; "-stats" and "-debug-only" require +Asserts. +; REQUIRES: asserts + +; Test import with smaller instruction limit +; RUN: opt -function-import -enable-import-metadata -summary-file %t3.thinlto.bc %t.bc -import-instr-limit=5 -S | FileCheck %s --check-prefix=CHECK --check-prefix=INSTLIM5 +; INSTLIM5-NOT: @staticfunc.llvm. + + +define i32 @main() #0 { +entry: + call void (...) @weakalias() + call void (...) @analias() + call void (...) @linkoncealias() + %call = call i32 (...) @referencestatics() + %call1 = call i32 (...) @referenceglobals() + %call2 = call i32 (...) @referencecommon() + call void (...) @setfuncptr() + call void (...) @callfuncptr() + call void (...) @weakfunc() + call void (...) @linkoncefunc2() + call void (...) @referencelargelinkonce() + call void (...) @variadic_no_va_start() + call void (...) @variadic_va_start() + ret i32 0 +} + +; Won't import weak alias +; CHECK-DAG: declare void @weakalias +declare void @weakalias(...) #1 + +; External alias imported as available_externally copy of aliasee +; CHECK-DAG: define available_externally void @analias +declare void @analias(...) #1 + +; External alias imported as available_externally copy of aliasee +; (linkoncealias is an external alias to a linkonce_odr) +declare void @linkoncealias(...) #1 +; CHECK-DAG: define available_externally void @linkoncealias() + +; INSTLIMDEF-DAG: Import referencestatics +; INSTLIMDEF-DAG: define available_externally i32 @referencestatics(i32 %i) !thinlto_src_module !0 { +; INSTLIM5-DAG: declare i32 @referencestatics(...) +declare i32 @referencestatics(...) #1 + +; The import of referencestatics will expose call to staticfunc that +; should in turn be imported as a promoted/renamed and hidden function. +; Ensure that the call is to the properly-renamed function. +; INSTLIMDEF-DAG: Import staticfunc +; INSTLIMDEF-DAG: %call = call i32 @staticfunc.llvm. +; INSTLIMDEF-DAG: define available_externally hidden i32 @staticfunc.llvm.{{.*}} !thinlto_src_module !0 { + +; INSTLIMDEF-DAG: Import referenceglobals +; CHECK-DAG: define available_externally i32 @referenceglobals(i32 %i) !thinlto_src_module !0 { +declare i32 @referenceglobals(...) #1 + +; The import of referenceglobals will expose call to globalfunc1 that +; should in turn be imported. +; INSTLIMDEF-DAG: Import globalfunc1 +; CHECK-DAG: define available_externally void @globalfunc1() !thinlto_src_module !0 + +; INSTLIMDEF-DAG: Import referencecommon +; CHECK-DAG: define available_externally i32 @referencecommon(i32 %i) !thinlto_src_module !0 { +declare i32 @referencecommon(...) #1 + +; INSTLIMDEF-DAG: Import setfuncptr +; CHECK-DAG: define available_externally void @setfuncptr() !thinlto_src_module !0 { +declare void @setfuncptr(...) #1 + +; INSTLIMDEF-DAG: Import callfuncptr +; CHECK-DAG: define available_externally void @callfuncptr() !thinlto_src_module !0 { +declare void @callfuncptr(...) #1 + +; Ensure that all uses of local variable @P which has used in setfuncptr +; and callfuncptr are to the same promoted/renamed global. +; CHECK-DAG: @P.llvm.{{.*}} = available_externally hidden global void ()* null +; CHECK-DAG: %0 = load void ()*, void ()** @P.llvm. +; CHECK-DAG: store void ()* @staticfunc2.llvm.{{.*}}, void ()** @P.llvm. + +; Ensure that @referencelargelinkonce definition is pulled in, but later we +; also check that the linkonceodr function is not. +; CHECK-DAG: define available_externally void @referencelargelinkonce() !thinlto_src_module !0 { +; INSTLIM5-DAG: declare void @linkonceodr() +declare void @referencelargelinkonce(...) + +; Won't import weak func +; CHECK-DAG: declare void @weakfunc(...) +declare void @weakfunc(...) #1 + +; Won't import linkonce func +; CHECK-DAG: declare void @linkoncefunc2(...) +declare void @linkoncefunc2(...) #1 + +; INSTLIMDEF-DAG: Import funcwithpersonality +; INSTLIMDEF-DAG: define available_externally hidden void @funcwithpersonality.llvm.{{.*}}() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) !thinlto_src_module !0 { +; INSTLIM5-DAG: declare hidden void @funcwithpersonality.llvm.{{.*}}() + +; We can import variadic functions without a va_start, since the inliner +; can handle them. +; INSTLIMDEF-DAG: Import variadic_no_va_start +; CHECK-DAG: define available_externally void @variadic_no_va_start(...) !thinlto_src_module !0 { +declare void @variadic_no_va_start(...) + +; We can import variadic functions with a va_start, since the inliner +; can sometimes handle them. +; CHECK-DAG: define available_externally void @variadic_va_start(...) +declare void @variadic_va_start(...) + +; INSTLIMDEF-DAG: Import globalfunc2 +; INSTLIMDEF-DAG: 15 function-import - Number of functions imported +; INSTLIMDEF-DAG: 4 function-import - Number of global variables imported + +; CHECK-DAG: !0 = !{!"{{.*}}/Inputs/funcimport.ll"} + +; The actual GUID values will depend on path to test. +; GUID-DAG: GUID {{.*}} is weakalias +; GUID-DAG: GUID {{.*}} is referenceglobals +; GUID-DAG: GUID {{.*}} is weakfunc +; GUID-DAG: GUID {{.*}} is main +; GUID-DAG: GUID {{.*}} is referencecommon +; GUID-DAG: GUID {{.*}} is analias +; GUID-DAG: GUID {{.*}} is referencestatics +; GUID-DAG: GUID {{.*}} is linkoncealias +; GUID-DAG: GUID {{.*}} is setfuncptr +; GUID-DAG: GUID {{.*}} is callfuncptr +; GUID-DAG: GUID {{.*}} is funcwithpersonality +; GUID-DAG: GUID {{.*}} is setfuncptr +; GUID-DAG: GUID {{.*}} is staticfunc2 +; GUID-DAG: GUID {{.*}} is __gxx_personality_v0 +; GUID-DAG: GUID {{.*}} is referencestatics +; GUID-DAG: GUID {{.*}} is globalfunc1 +; GUID-DAG: GUID {{.*}} is globalfunc2 +; GUID-DAG: GUID {{.*}} is P +; GUID-DAG: GUID {{.*}} is staticvar +; GUID-DAG: GUID {{.*}} is commonvar +; GUID-DAG: GUID {{.*}} is weakalias +; GUID-DAG: GUID {{.*}} is staticfunc +; GUID-DAG: GUID {{.*}} is weakfunc +; GUID-DAG: GUID {{.*}} is referenceglobals +; GUID-DAG: GUID {{.*}} is weakvar +; GUID-DAG: GUID {{.*}} is staticconstvar +; GUID-DAG: GUID {{.*}} is analias +; GUID-DAG: GUID {{.*}} is globalvar +; GUID-DAG: GUID {{.*}} is referencecommon +; GUID-DAG: GUID {{.*}} is linkoncealias +; GUID-DAG: GUID {{.*}} is callfuncptr +; GUID-DAG: GUID {{.*}} is linkoncefunc + +; DUMP: Module [[M1:.*]] imports from 1 module +; DUMP-NEXT: 15 functions imported from [[M2:.*]] +; DUMP-NEXT: 4 vars imported from [[M2]] +; DUMP: Imported 15 functions for Module [[M1]] +; DUMP-NEXT: Imported 4 global variables for Module [[M1]] diff --git a/llvm/test/Transforms/FunctionImport/funcimport_alias.ll b/llvm/test/Transforms/FunctionImport/funcimport_alias.ll new file mode 100644 index 00000000000..7868e08d32f --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/funcimport_alias.ll @@ -0,0 +1,25 @@ +; Do setup work for all below tests: generate bitcode and combined index +; RUN: opt -module-summary %s -o %t.bc +; RUN: opt -module-summary %p/Inputs/funcimport_alias.ll -o %t2.bc +; RUN: llvm-lto -thinlto -o %t3 %t.bc %t2.bc + +; Do the import now. Ensures that the importer handles an external call +; from imported callanalias() to a function that is defined already in +; the dest module, but as an alias. +; RUN: opt -function-import -summary-file %t3.thinlto.bc %t.bc -S | FileCheck %s + +define i32 @main() #0 { +entry: + call void @callanalias() + ret i32 0 +} + +@analias = alias void (), void ()* @globalfunc + +define void @globalfunc() #0 { +entry: + ret void +} + +declare void @callanalias() #1 +; CHECK-DAG: define available_externally void @callanalias() diff --git a/llvm/test/Transforms/FunctionImport/funcimport_cutoff.ll b/llvm/test/Transforms/FunctionImport/funcimport_cutoff.ll new file mode 100644 index 00000000000..dac4bd14faa --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/funcimport_cutoff.ll @@ -0,0 +1,49 @@ +; Test to ensure that thin linking with -import-cutoff stops importing when +; expected. + +; "-stats" and "-debug-only" require +Asserts. +; REQUIRES: asserts + +; RUN: opt -module-summary %s -o %t.bc +; RUN: opt -module-summary %p/Inputs/funcimport_cutoff.ll -o %t2.bc +; RUN: llvm-lto -thinlto -o %t3 %t.bc %t2.bc + +; First do with default options, which should import both foo and bar +; RUN: opt -function-import -stats -print-imports -summary-file %t3.thinlto.bc %t.bc -S 2>&1 | FileCheck %s --check-prefix=IMPORT + +; Next try to restrict to 1 import. This should import just foo. +; RUN: opt -import-cutoff=1 -function-import -stats -print-imports -summary-file %t3.thinlto.bc %t.bc -S 2>&1 | FileCheck %s --check-prefix=IMPORT1 + +; Next try to restrict to 0 imports. This should not import. +; RUN: opt -import-cutoff=0 -function-import -stats -print-imports -summary-file %t3.thinlto.bc %t.bc -S 2>&1 | FileCheck %s --check-prefix=NOIMPORT + +define i32 @main() { +entry: + call void @foo() + call void @bar() + ret i32 0 +} + +declare void @foo() +declare void @bar() + +; Check -print-imports output +; IMPORT: Import foo +; IMPORT: Import bar +; IMPORT1: Import foo +; IMPORT1-NOT: Import bar +; NOIMPORT-NOT: Import foo +; NOIMPORT-NOT: Import bar + +; Check -S output +; IMPORT-DAG: define available_externally void @foo() +; IMPORT-DAG: define available_externally void @bar() +; NOIMPORT-DAG: declare void @foo() +; NOIMPORT-DAG: declare void @bar() +; IMPORT1-DAG: define available_externally void @foo() +; IMPORT1-DAG: declare void @bar() + +; Check -stats output +; IMPORT: 2 function-import - Number of functions imported +; IMPORT1: 1 function-import - Number of functions imported +; NOIMPORT-NOT: function-import - Number of functions imported diff --git a/llvm/test/Transforms/FunctionImport/funcimport_debug.ll b/llvm/test/Transforms/FunctionImport/funcimport_debug.ll new file mode 100644 index 00000000000..e764d78c300 --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/funcimport_debug.ll @@ -0,0 +1,53 @@ +; Do setup work for all below tests: generate bitcode and combined index +; RUN: opt -module-summary %s -o %t.bc +; RUN: opt -module-summary %p/Inputs/funcimport_debug.ll -o %t2.bc +; RUN: llvm-lto -thinlto -o %t3 %t.bc %t2.bc + +; Do the import now and confirm that metadata is linked for imported function. +; RUN: opt -function-import -summary-file %t3.thinlto.bc %t.bc -S | FileCheck %s + +; CHECK: define available_externally void @func() + +; Check that we have exactly two subprograms (that func's subprogram wasn't +; linked more than once for example), and that they are connected to +; the correct compile unit. +; CHECK: ![[CU1:[0-9]+]] = distinct !DICompileUnit( +; CHECK: ![[CU2:[0-9]+]] = distinct !DICompileUnit( +; CHECK: distinct !DISubprogram(name: "main" +; CHECK-SAME: unit: ![[CU1]] +; CHECK: distinct !DISubprogram(name: "func" +; CHECK-SAME: unit: ![[CU2]] +; CHECK-NOT: distinct !DISubprogram + +; ModuleID = 'funcimport_debug.o' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: nounwind uwtable +define i32 @main() #0 !dbg !4 { +entry: + call void (...) @func(), !dbg !11 + ret i32 0, !dbg !12 +} + +declare void @func(...) #1 + +attributes #0 = { nounwind uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!8, !9} +!llvm.ident = !{!10} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 3.8.0 (trunk 255685) (llvm/trunk 255682)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "funcimport_debug.c", directory: ".") +!2 = !{} +!4 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 2, type: !5, isLocal: false, isDefinition: true, scopeLine: 2, isOptimized: false, unit: !0, retainedNodes: !2) +!5 = !DISubroutineType(types: !6) +!6 = !{!7} +!7 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed) +!8 = !{i32 2, !"Dwarf Version", i32 4} +!9 = !{i32 2, !"Debug Info Version", i32 3} +!10 = !{!"clang version 3.8.0 (trunk 255685) (llvm/trunk 255682)"} +!11 = !DILocation(line: 3, column: 3, scope: !4) +!12 = !DILocation(line: 4, column: 1, scope: !4) diff --git a/llvm/test/Transforms/FunctionImport/funcimport_forcecold.ll b/llvm/test/Transforms/FunctionImport/funcimport_forcecold.ll new file mode 100644 index 00000000000..a9aa77f9afe --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/funcimport_forcecold.ll @@ -0,0 +1,36 @@ +; Test to ensure that building summary with -force-summary-edges-cold +; blocks importing as expected. + +; "-stats" and "-debug-only" require +Asserts. +; REQUIRES: asserts + +; First do with default options, which should import +; RUN: opt -module-summary %s -o %t.bc +; RUN: opt -module-summary %p/Inputs/funcimport_forcecold.ll -o %t2.bc +; RUN: llvm-lto -thinlto -o %t3 %t.bc %t2.bc +; RUN: opt -function-import -stats -print-imports -summary-file %t3.thinlto.bc %t.bc -S 2>&1 | FileCheck %s --check-prefix=IMPORT + +; Next rebuild caller module summary with non-critical edges forced cold (which +; should affect all edges in this test as we don't have any sample pgo). +; Make sure we don't import. +; RUN: opt -force-summary-edges-cold=all-non-critical -module-summary %s -o %t.bc +; RUN: llvm-lto -thinlto -o %t3 %t.bc %t2.bc +; RUN: opt -function-import -stats -print-imports -summary-file %t3.thinlto.bc %t.bc -S 2>&1 | FileCheck %s --check-prefix=NOIMPORT + +; Next rebuild caller module summary with all edges forced cold. +; Make sure we don't import. +; RUN: opt -force-summary-edges-cold=all -module-summary %s -o %t.bc +; RUN: llvm-lto -thinlto -o %t3 %t.bc %t2.bc +; RUN: opt -function-import -stats -print-imports -summary-file %t3.thinlto.bc %t.bc -S 2>&1 | FileCheck %s --check-prefix=NOIMPORT + +define i32 @main() { +entry: + call void @foo() + ret i32 0 +} + +; IMPORT: Import foo +; NOIMPORT-NOT: Import foo +; IMPORT: define available_externally void @foo() +; NOIMPORT: declare void @foo() +declare void @foo() diff --git a/llvm/test/Transforms/FunctionImport/funcimport_forcecold_samplepgo.ll b/llvm/test/Transforms/FunctionImport/funcimport_forcecold_samplepgo.ll new file mode 100644 index 00000000000..4ff02b9ba0f --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/funcimport_forcecold_samplepgo.ll @@ -0,0 +1,37 @@ +; Test to ensure that building summary with -force-summary-edges-cold +; blocks importing as expected. + +; "-stats" and "-debug-only" require +Asserts. +; REQUIRES: asserts + +; First do with default options, which should import +; RUN: opt -module-summary %s -o %t.bc +; RUN: opt -module-summary %p/Inputs/funcimport_forcecold.ll -o %t2.bc +; RUN: llvm-lto -thinlto -o %t3 %t.bc %t2.bc +; RUN: opt -function-import -stats -print-imports -summary-file %t3.thinlto.bc %t.bc -S 2>&1 | FileCheck %s --check-prefix=IMPORT + +; Next rebuild caller module summary with only non-critical edges forced cold, +; which should still import in this case. +; RUN: opt -force-summary-edges-cold=all-non-critical -module-summary %s -o %t.bc +; RUN: llvm-lto -thinlto -o %t3 %t.bc %t2.bc +; RUN: opt -function-import -stats -print-imports -summary-file %t3.thinlto.bc %t.bc -S 2>&1 | FileCheck %s --check-prefix=IMPORT + +; Next rebuild caller module summary with all edges forced cold. +; Make sure we don't import. +; RUN: opt -force-summary-edges-cold=all -module-summary %s -o %t.bc +; RUN: llvm-lto -thinlto -o %t3 %t.bc %t2.bc +; RUN: opt -function-import -stats -print-imports -summary-file %t3.thinlto.bc %t.bc -S 2>&1 | FileCheck %s --check-prefix=NOIMPORT + +define i32 @main() !prof !1 { +entry: + call void @foo() + ret i32 0 +} + +; IMPORT: Import foo +; NOIMPORT-NOT: Import foo +; IMPORT: define available_externally void @foo() +; NOIMPORT: declare void @foo() +declare void @foo() + +!1 = !{!"function_entry_count", i64 110, i64 6699318081062747564} diff --git a/llvm/test/Transforms/FunctionImport/funcimport_resolved.ll b/llvm/test/Transforms/FunctionImport/funcimport_resolved.ll new file mode 100644 index 00000000000..b256a613602 --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/funcimport_resolved.ll @@ -0,0 +1,52 @@ +; Test to ensure that we always select the same copy of a linkonce function +; when it is encountered with different thresholds. When we encounter the +; copy in funcimport_resolved1.ll with a higher threshold via the direct call +; from main(), it will be selected for importing. When we encounter it with a +; lower threshold by reaching it from the deeper call chain via foo(), it +; won't be selected for importing. We don't want to select both the copy from +; funcimport_resolved1.ll and the smaller one from funcimport_resolved2.ll, +; leaving it up to the backend to figure out which one to actually import. +; The linkonce_odr may have different instruction counts in practice due to +; different inlines in the compile step. + +; Require asserts so we can use -debug-only +; REQUIRES: asserts + +; REQUIRES: x86-registered-target + +; RUN: opt -module-summary %s -o %t.bc +; RUN: opt -module-summary %p/Inputs/funcimport_resolved1.ll -o %t2.bc +; RUN: opt -module-summary %p/Inputs/funcimport_resolved2.ll -o %t3.bc + +; First do a sanity check that all callees are imported with the default +; instruction limit +; RUN: llvm-lto2 run %t.bc %t2.bc %t3.bc -o %t4 -r=%t.bc,_main,pl -r=%t.bc,_linkonceodrfunc,l -r=%t.bc,_foo,l -r=%t2.bc,_foo,pl -r=%t2.bc,_linkonceodrfunc,pl -r=%t2.bc,_linkonceodrfunc2,pl -r=%t3.bc,_linkonceodrfunc,l -thinlto-threads=1 -debug-only=function-import 2>&1 | FileCheck %s --check-prefix=INSTLIMDEFAULT +; INSTLIMDEFAULT: Is importing function {{.*}} foo from {{.*}}funcimport_resolved1.ll +; INSTLIMDEFAULT: Is importing function {{.*}} linkonceodrfunc from {{.*}}funcimport_resolved1.ll +; INSTLIMDEFAULT: Is importing function {{.*}} linkonceodrfunc2 from {{.*}}funcimport_resolved1.ll +; INSTLIMDEFAULT: Is importing function {{.*}} f from {{.*}}funcimport_resolved1.ll +; INSTLIMDEFAULT-NOT: Is importing function {{.*}} linkonceodrfunc from {{.*}}funcimport_resolved2.ll + +; Now run with the lower threshold that will only allow linkonceodrfunc to be +; imported from funcimport_resolved1.ll when encountered via the direct call +; from main(). Ensure we don't also select the copy in funcimport_resolved2.ll +; when it is encountered via the deeper call chain. +; RUN: llvm-lto2 run %t.bc %t2.bc %t3.bc -o %t4 -r=%t.bc,_main,pl -r=%t.bc,_linkonceodrfunc,l -r=%t.bc,_foo,l -r=%t2.bc,_foo,pl -r=%t2.bc,_linkonceodrfunc,pl -r=%t2.bc,_linkonceodrfunc2,pl -r=%t3.bc,_linkonceodrfunc,l -thinlto-threads=1 -debug-only=function-import -import-instr-limit=8 2>&1 | FileCheck %s --check-prefix=INSTLIM8 +; INSTLIM8: Is importing function {{.*}} foo from {{.*}}funcimport_resolved1.ll +; INSTLIM8: Is importing function {{.*}} linkonceodrfunc from {{.*}}funcimport_resolved1.ll +; INSTLIM8: Not importing function {{.*}} linkonceodrfunc2 from {{.*}}funcimport_resolved1.ll +; INSTLIM8: Is importing function {{.*}} f from {{.*}}funcimport_resolved1.ll +; INSTLIM8-NOT: Is importing function {{.*}} linkonceodrfunc from {{.*}}funcimport_resolved2.ll + +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +define i32 @main() #0 { +entry: + call void (...) @foo() + call void (...) @linkonceodrfunc() + ret i32 0 +} + +declare void @foo(...) #1 +declare void @linkonceodrfunc(...) #1 diff --git a/llvm/test/Transforms/FunctionImport/funcimport_var.ll b/llvm/test/Transforms/FunctionImport/funcimport_var.ll new file mode 100644 index 00000000000..edd874e6297 --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/funcimport_var.ll @@ -0,0 +1,27 @@ +; This test makes sure a static var is not selected as a callee target +; (which will crash compilation). +; RUN: opt -module-summary %s -o %t.bc +; RUN: opt -module-summary %p/Inputs/funcimport_var2.ll -o %t2.bc +; RUN: llvm-lto -thinlto -thinlto-action=thinlink -o %t3 %t.bc %t2.bc +; RUN: llvm-lto -thinlto -thinlto-action=import -thinlto-index=%t3 %t.bc %t2.bc +; RUN: llvm-lto -thinlto -thinlto-action=run %t.bc %t2.bc -exported-symbol=_Z4LinkPKcS0_ +; RUN: llvm-nm %t.bc.thinlto.o | FileCheck %s +; RUN: llvm-lto2 run %t.bc %t2.bc -o %t.out \ +; RUN: -r %t.bc,_Z4LinkPKcS0_,plx \ +; RUN: -r %t.bc,link,l \ +; RUN: -r %t2.bc,get_link,plx +; RUN: llvm-nm %t.out.1 | FileCheck %s +; CHECK: U link + +; REQUIRES: x86-registered-target + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define i32 @_Z4LinkPKcS0_(i8*, i8*) local_unnamed_addr { + %3 = tail call i32 @link(i8* %0, i8* %1) #2 + ret i32 %3 +} + +; Function Attrs: nounwind +declare i32 @link(i8*, i8*) local_unnamed_addr diff --git a/llvm/test/Transforms/FunctionImport/hotness_based_import.ll b/llvm/test/Transforms/FunctionImport/hotness_based_import.ll new file mode 100644 index 00000000000..9de8714072d --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/hotness_based_import.ll @@ -0,0 +1,137 @@ +; Test to check the callgraph in summary when there is PGO +; RUN: opt -module-summary %s -o %t.bc +; RUN: opt -module-summary %p/Inputs/hotness_based_import.ll -o %t2.bc +; RUN: llvm-lto -thinlto -o %t3 %t.bc %t2.bc + +; Test import with default hot multiplier (3) and default hot-evolution-factor (1.0) +; RUN: opt -function-import -summary-file %t3.thinlto.bc %t.bc -import-instr-limit=1 --S | FileCheck %s --check-prefix=CHECK --check-prefix=HOT-DEFAULT +; RUN: opt -function-import -summary-file %t3.thinlto.bc %t.bc -import-instr-limit=1 --S -import-hot-multiplier=3.0 -import-cold-multiplier=0.0 | FileCheck %s --check-prefix=CHECK --check-prefix=HOT-DEFAULT +; RUN: opt -function-import -summary-file %t3.thinlto.bc %t.bc -import-instr-limit=1 --S -import-hot-multiplier=3.0 | FileCheck %s --check-prefix=CHECK --check-prefix=HOT-DEFAULT +; RUN: opt -function-import -summary-file %t3.thinlto.bc %t.bc -import-instr-limit=1 --S -import-hot-multiplier=3.0 -import-instr-evolution-factor=0.0 | FileCheck %s --check-prefix=CHECK --check-prefix=HOT-DEFAULT +; RUN: opt -function-import -summary-file %t3.thinlto.bc %t.bc -import-instr-limit=1 --S -import-hot-multiplier=3.0 -import-hot-evolution-factor=1.0 | FileCheck %s --check-prefix=CHECK --check-prefix=HOT-DEFAULT +; HOT-DEFAULT-DAG: define available_externally void @hot1() +; HOT-DEFAULT-DAG: define available_externally void @hot2() +; HOT-DEFAULT-DAG: define available_externally void @calledFromHot() +; HOT-DEFAULT-DAG: define available_externally void @calledFromHot2() +; HOT-DEFAULT-DAG: define available_externally void @none1() +; HOT-DEFAULT-NOT: define available_externally void @cold() +; HOT-DEFAULT-NOT: define available_externally void @hot3() +; HOT-DEFAULT-NOT: define available_externally void @none2() +; HOT-DEFAULT-NOT: define available_externally void @none3() +; HOT-DEFAULT-NOT: define available_externally void @cold2() +; HOT-DEFAULT-NOT: define available_externally void @calledFromCold() +; HOT-DEFAULT-NOT: define available_externally void @calledFromNone() + +; This one tests if we decay threshold for hot callsites. With hot-evolution-factor of 0 +; we should not import any of calledFromHot functions +; RUN: opt -function-import -summary-file %t3.thinlto.bc %t.bc -import-instr-limit=1 --S -import-hot-multiplier=3.0 -import-hot-evolution-factor=0.0 | FileCheck %s --check-prefix=CHECK --check-prefix=HOT-EVOLUTION +; HOT-EVOLUTION-DAG: define available_externally void @hot1() +; HOT-EVOLUTION-DAG: define available_externally void @hot2() +; HOT-EVOLUTION-DAG: define available_externally void @none1() +; HOT-EVOLUTION-NOT: define available_externally void @hot3() +; HOT-EVOLUTION-NOT: define available_externally void @cold() +; HOT-EVOLUTION-NOT: define available_externally void @none2() +; HOT-EVOLUTION-NOT: define available_externally void @none3() +; HOT-EVOLUTION-NOT: define available_externally void @cold2() +; HOT-EVOLUTION-NOT: define available_externally void @calledFromHot() +; HOT-EVOLUTION-NOT: define available_externally void @calledFromHot2() + +; Test import with hot multiplier 1.0 - treat hot callsites as normal. +; RUN: opt -function-import -summary-file %t3.thinlto.bc %t.bc -import-instr-limit=1 -import-hot-multiplier=1.0 --S | FileCheck %s --check-prefix=CHECK --check-prefix=HOT-ONE +; HOT-ONE-DAG: define available_externally void @hot1() +; HOT-ONE-DAG: define available_externally void @none1() +; HOT-ONE-NOT: define available_externally void @cold() +; HOT-ONE-NOT: define available_externally void @hot2() +; HOT-ONE-NOT: define available_externally void @hot3() +; HOT-ONE-NOT: define available_externally void @none2() +; HOT-ONE-NOT: define available_externally void @none3() +; HOT-ONE-NOT: define available_externally void @cold2() + +; RUN: opt -function-import -summary-file %t3.thinlto.bc %t.bc -import-instr-limit=1 -import-hot-multiplier=1.0 -import-cold-multiplier=1.0 --S | FileCheck %s --check-prefix=CHECK --check-prefix=HOT-COLD-ONE +; HOT-COLD-ONE-DAG: define available_externally void @hot1() +; HOT-COLD-ONE-DAG: define available_externally void @cold() +; HOT-COLD-ONE-DAG: define available_externally void @none1() +; HOT-COLD-ONE-NOT: define available_externally void @hot2() +; HOT-COLD-ONE-NOT: define available_externally void @hot3() +; HOT-COLD-ONE-NOT: define available_externally void @none2() +; HOT-COLD-ONE-NOT: define available_externally void @none3() +; HOT-COLD-ONE-NOT: define available_externally void @cold2() + +; Test import with hot multiplier 0.0 and high threshold - don't import functions called from hot callsite. +; RUN: opt -function-import -summary-file %t3.thinlto.bc %t.bc -import-instr-limit=10 -import-hot-multiplier=0.0 -import-cold-multiplier=1.0 --S | FileCheck %s --check-prefix=CHECK --check-prefix=HOT-ZERO +; HOT-ZERO-DAG: define available_externally void @cold() +; HOT-ZERO-DAG: define available_externally void @none1() +; HOT-ZERO-DAG: define available_externally void @none2() +; HOT-ZERO-DAG: define available_externally void @none3() +; HOT-ZERO-DAG: define available_externally void @cold2() +; HOT-ZERO-DAG: define available_externally void @calledFromCold() +; HOT-ZERO-DAG: define available_externally void @calledFromNone() +; HOT-ZERO-NOT: define available_externally void @hot2() +; HOT-ZERO-NOT: define available_externally void @hot1() +; HOT-ZERO-NOT: define available_externally void @hot3() +; HOT-ZERO-NOT: define available_externally void @calledFromHot() +; HOT-ZERO-NOT: define available_externally void @calledFromHot2() + + +; ModuleID = 'thinlto-function-summary-callgraph.ll' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; This function have high profile count, so entry block is hot. +define void @hot_function(i1 %a, i1 %a2) !prof !20 { +entry: + call void @hot1() + br i1 %a, label %Cold, label %Hot, !prof !41 +Cold: ; 1/1000 goes here + call void @cold() + call void @cold2() + call void @hot2() + call void @none1() + br label %exit +Hot: ; 999/1000 goes here + call void @hot2() + call void @hot3() + br i1 %a2, label %None1, label %None2, !prof !42 +None1: ; half goes here + call void @none1() + call void @none2() + br label %exit +None2: ; half goes here + call void @none3() + br label %exit +exit: + ret void +} + +declare void @hot1() #1 +declare void @hot2() #1 +declare void @hot3() #1 +declare void @cold() #1 +declare void @cold2() #1 +declare void @none1() #1 +declare void @none2() #1 +declare void @none3() #1 + + +!41 = !{!"branch_weights", i32 1, i32 1000} +!42 = !{!"branch_weights", i32 1, i32 1} + + + +!llvm.module.flags = !{!1} +!20 = !{!"function_entry_count", i64 110} + +!1 = !{i32 1, !"ProfileSummary", !2} +!2 = !{!3, !4, !5, !6, !7, !8, !9, !10} +!3 = !{!"ProfileFormat", !"InstrProf"} +!4 = !{!"TotalCount", i64 10000} +!5 = !{!"MaxCount", i64 10} +!6 = !{!"MaxInternalCount", i64 1} +!7 = !{!"MaxFunctionCount", i64 1000} +!8 = !{!"NumCounts", i64 3} +!9 = !{!"NumFunctions", i64 3} +!10 = !{!"DetailedSummary", !11} +!11 = !{!12, !13, !14} +!12 = !{i32 10000, i64 100, i32 1} +!13 = !{i32 999000, i64 100, i32 1} +!14 = !{i32 999999, i64 1, i32 2} diff --git a/llvm/test/Transforms/FunctionImport/import_stats.ll b/llvm/test/Transforms/FunctionImport/import_stats.ll new file mode 100644 index 00000000000..2cb415d1e96 --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/import_stats.ll @@ -0,0 +1,71 @@ +; Test to check thin link importing stats + +; -stats requires asserts +; REQUIRES: asserts + +; REQUIRES: x86-registered-target + +; RUN: opt -module-summary %s -o %t.bc +; RUN: opt -module-summary %p/Inputs/import_stats.ll -o %t2.bc + +; Test thin link stats with both new and old LTO +; RUN: llvm-lto -thinlto-action=run -stats %t.bc %t2.bc \ +; RUN: 2>&1 | FileCheck %s --check-prefix=THINLINKSTATS +; RUN: llvm-lto2 run -stats -o %t3 %t.bc %t2.bc \ +; RUN: -r %t.bc,hot_function,plx \ +; RUN: -r %t.bc,hot, \ +; RUN: -r %t.bc,critical, \ +; RUN: -r %t.bc,none, \ +; RUN: -r %t2.bc,hot,plx \ +; RUN: -r %t2.bc,critical,plx \ +; RUN: -r %t2.bc,none,plx \ +; RUN: -r %t2.bc,globalvar,plx \ +; RUN: 2>&1 | FileCheck %s --check-prefix=THINLINKSTATS + +; THINLINKSTATS-DAG: 1 function-import - Number of global variables thin link decided to import +; THINLINKSTATS-DAG: 1 function-import - Number of critical functions thin link decided to import +; THINLINKSTATS-DAG: 3 function-import - Number of functions thin link decided to import +; THINLINKSTATS-DAG: 1 function-import - Number of hot functions thin link decided to import + +; ModuleID = 'import_stats.ll' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; This function has a high profile count, so entry block is hot. +define void @hot_function(i1 %a) !prof !20 { +entry: + call void @hot() + call void @critical() + br i1 %a, label %None1, label %None2, !prof !42 +None1: ; half goes here + call void @none() + br label %exit +None2: ; half goes here + br label %exit +exit: + ret void +} + +declare void @hot() +declare void @none() +declare void @critical() + +!42 = !{!"branch_weights", i32 1, i32 1} + +!llvm.module.flags = !{!1} +!20 = !{!"function_entry_count", i64 100, i64 696010031887058302} + +!1 = !{i32 1, !"ProfileSummary", !2} +!2 = !{!3, !4, !5, !6, !7, !8, !9, !10} +!3 = !{!"ProfileFormat", !"InstrProf"} +!4 = !{!"TotalCount", i64 300} +!5 = !{!"MaxCount", i64 100} +!6 = !{!"MaxInternalCount", i64 100} +!7 = !{!"MaxFunctionCount", i64 100} +!8 = !{!"NumCounts", i64 4} +!9 = !{!"NumFunctions", i64 1} +!10 = !{!"DetailedSummary", !11} +!11 = !{!12, !13, !14} +!12 = !{i32 10000, i64 100, i32 1} +!13 = !{i32 999000, i64 100, i32 1} +!14 = !{i32 999999, i64 1, i32 4} diff --git a/llvm/test/Transforms/FunctionImport/inlineasm.ll b/llvm/test/Transforms/FunctionImport/inlineasm.ll new file mode 100644 index 00000000000..de6fd7a9cfc --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/inlineasm.ll @@ -0,0 +1,19 @@ +; Do setup work for all below tests: generate bitcode and combined index +; RUN: opt -module-summary %s -o %t.bc +; RUN: opt -module-summary %p/Inputs/inlineasm.ll -o %t2.bc +; RUN: llvm-lto -thinlto -o %t3 %t.bc %t2.bc + +; Attempt the import now, ensure below that file containing inline assembly +; is not imported from. Otherwise we would need to promote its local variable +; used in the inline assembly, which would not see the rename. +; RUN: opt -function-import -summary-file %t3.thinlto.bc %t.bc -S 2>&1 | FileCheck %s + +define i32 @main() #0 { +entry: + %f = alloca i64, align 8 + call void @foo(i64* %f) + ret i32 0 +} + +; CHECK: declare void @foo(i64*) +declare void @foo(i64*) #1 diff --git a/llvm/test/Transforms/FunctionImport/not-prevailing.ll b/llvm/test/Transforms/FunctionImport/not-prevailing.ll new file mode 100644 index 00000000000..4412715e043 --- /dev/null +++ b/llvm/test/Transforms/FunctionImport/not-prevailing.ll @@ -0,0 +1,18 @@ +; RUN: opt -module-summary %s -o %t1.bc +; RUN: opt -module-summary -o %t2.bc %S/Inputs/not-prevailing.ll +; RUN: not llvm-lto2 run -o %t3.bc %t1.bc %t2.bc -r %t1.bc,bar,px \ +; RUN: -r %t1.bc,foo,x -r %t2.bc,foo,x -save-temps 2>&1 | FileCheck %s + +; CHECK: Interposable and available_externally/linkonce_odr/weak_odr symbol + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define available_externally i32 @foo() { + ret i32 1 +} + +define i32 @bar() { + %1 = call i32 @foo() + ret i32 %1 +} |