diff --git a/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c b/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c index becfe1fd9f5ad8..4e3230bfb24c31 100644 --- a/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c +++ b/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c @@ -24,27 +24,21 @@ #define PROF_VNODES_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_VNODES_COMMON) /* Declare section start and stop symbols for various sections - * generated by compiler instrumentation. + * generated by compiler instrumentation. These symbols are + * declared as weak, which means that they'll end up as NULL if + * those sections are empty, and any code that iterates over the + * content using __llvm_profile_begin_* and __llvm_profile_end_* + * functions defined below will be a no-op. */ -extern __llvm_profile_data PROF_DATA_START COMPILER_RT_VISIBILITY; -extern __llvm_profile_data PROF_DATA_STOP COMPILER_RT_VISIBILITY; -extern uint64_t PROF_CNTS_START COMPILER_RT_VISIBILITY; -extern uint64_t PROF_CNTS_STOP COMPILER_RT_VISIBILITY; -extern uint32_t PROF_ORDERFILE_START COMPILER_RT_VISIBILITY; -extern char PROF_NAME_START COMPILER_RT_VISIBILITY; -extern char PROF_NAME_STOP COMPILER_RT_VISIBILITY; -extern ValueProfNode PROF_VNODES_START COMPILER_RT_VISIBILITY; -extern ValueProfNode PROF_VNODES_STOP COMPILER_RT_VISIBILITY; - -/* Add dummy data to ensure the section is always created. */ -__llvm_profile_data - __prof_data_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_DATA_SECT_NAME); -uint64_t - __prof_cnts_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_CNTS_SECT_NAME); -uint32_t - __prof_orderfile_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_ORDERFILE_SECT_NAME); -char __prof_nms_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_NAME_SECT_NAME); -ValueProfNode __prof_vnodes_sect_data[0] COMPILER_RT_SECTION(INSTR_PROF_VNODES_SECT_NAME); +extern __llvm_profile_data PROF_DATA_START COMPILER_RT_VISIBILITY COMPILER_RT_WEAK; +extern __llvm_profile_data PROF_DATA_STOP COMPILER_RT_VISIBILITY COMPILER_RT_WEAK; +extern uint64_t PROF_CNTS_START COMPILER_RT_VISIBILITY COMPILER_RT_WEAK; +extern uint64_t PROF_CNTS_STOP COMPILER_RT_VISIBILITY COMPILER_RT_WEAK; +extern uint32_t PROF_ORDERFILE_START COMPILER_RT_VISIBILITY COMPILER_RT_WEAK; +extern char PROF_NAME_START COMPILER_RT_VISIBILITY COMPILER_RT_WEAK; +extern char PROF_NAME_STOP COMPILER_RT_VISIBILITY COMPILER_RT_WEAK; +extern ValueProfNode PROF_VNODES_START COMPILER_RT_VISIBILITY COMPILER_RT_WEAK; +extern ValueProfNode PROF_VNODES_STOP COMPILER_RT_VISIBILITY COMPILER_RT_WEAK; COMPILER_RT_VISIBILITY const __llvm_profile_data * __llvm_profile_begin_data(void) { diff --git a/compiler-rt/test/profile/instrprof-gc-sections.c b/compiler-rt/test/profile/instrprof-gc-sections.c new file mode 100644 index 00000000000000..a3d0fa466c30e8 --- /dev/null +++ b/compiler-rt/test/profile/instrprof-gc-sections.c @@ -0,0 +1,91 @@ +// REQUIRES: linux, lld-available + +// RUN: %clang_profgen=%t.profraw -fuse-ld=lld -fcoverage-mapping -mllvm -enable-name-compression=false -DCODE=1 -ffunction-sections -fdata-sections -Wl,--gc-sections -o %t %s +// RUN: %run %t +// RUN: llvm-profdata merge -o %t.profdata %t.profraw +// RUN: llvm-profdata show --all-functions %t.profdata | FileCheck %s -check-prefix=PROF +// RUN: llvm-cov show %t -instr-profile %t.profdata | FileCheck %s -check-prefix=COV +// RUN: llvm-nm %t | FileCheck %s -check-prefix=NM +// RUN: llvm-readelf -x __llvm_prf_names %t | FileCheck %s -check-prefix=PRF_NAMES +// RUN: llvm-readelf -x __llvm_prf_cnts %t | FileCheck %s -check-prefix=PRF_CNTS + +// RUN: %clang_lto_profgen=%t.lto.profraw -fuse-ld=lld -fcoverage-mapping -mllvm -enable-name-compression=false -DCODE=1 -ffunction-sections -fdata-sections -Wl,--gc-sections -flto -o %t.lto %s +// RUN: %run %t.lto +// RUN: llvm-profdata merge -o %t.lto.profdata %t.lto.profraw +// RUN: llvm-profdata show --all-functions %t.lto.profdata | FileCheck %s -check-prefix=PROF +// RUN: llvm-cov show %t.lto -instr-profile %t.lto.profdata | FileCheck %s -check-prefix=COV +// RUN: llvm-nm %t.lto | FileCheck %s -check-prefix=NM +// RUN: llvm-readelf -x __llvm_prf_names %t.lto | FileCheck %s -check-prefix=PRF_NAMES +// RUN: llvm-readelf -x __llvm_prf_cnts %t.lto | FileCheck %s -check-prefix=PRF_CNTS + +// Note: We expect foo() and some of the profiling data associated with it to +// be garbage collected. + +// Note: When there is no code in a program, we expect to see the exact same +// set of external functions provided by the profile runtime. + +// RUN: %clang_profgen -fcoverage-mapping -ffunction-sections -fdata-sections -Wl,--gc-sections -shared -o %t.nocode.so %s +// RUN: llvm-nm -jgU %t.nocode.so | grep -vE "__start_.*|__stop_.*" > %t.nocode.syms +// RUN: llvm-nm -jgU %t | grep -vE "main|foo|_start|__libc_.*" > %t.code.syms +// RUN: diff %t.nocode.syms %t.code.syms + +// Note: We also check the IR instrumentation and expect foo() to be garbage +// collected as well. + +// RUN: %clang_pgogen=%t.pgo.profraw -fuse-ld=lld -DCODE=1 -ffunction-sections -fdata-sections -Wl,--gc-sections -o %t.pgo %s +// RUN: %run %t.pgo +// RUN: llvm-profdata merge -o %t.pgo.profdata %t.pgo.profraw +// RUN: llvm-profdata show --all-functions %t.pgo.profdata | FileCheck %s -check-prefix=PGO +// RUN: llvm-nm %t.pgo | FileCheck %s -check-prefix=NM + +#ifdef CODE + +// COV: [[@LINE+1]]{{ *}}|{{ *}}0|void foo() +void foo() {} + +// COV: [[@LINE+1]]{{ *}}|{{ *}}1|int main +int main() { return 0; } + +#endif // CODE + +// NM-NOT: foo + +// PROF: Counters: +// PROF-NEXT: main: +// PROF-NEXT: Hash: +// PROF-NEXT: Counters: 1 +// PROF-NEXT: Function count: 1 +// PROF-NEXT: Instrumentation level: Front-end +// PROF-NEXT: Functions shown: 1 +// PROF-NEXT: Total functions: 1 +// PROF-NEXT: Maximum function count: +// PROF-NEXT: Maximum internal block count: + +// Note: We don't expect the names of garbage collected functions to disappear +// from __llvm_prf_names, because collectPGOFuncNameStrings() glues the names +// together. + +// PRF_NAMES: Hex dump of section '__llvm_prf_names': +// PRF_NAMES-NEXT: {{.*}} 0800666f 6f016d61 696e {{.*$}} +// | | f o o # m a i n +// | |___________| +// | | +// UncompressedLen = 8 | +// | +// CompressedLen = 0 + +// Note: We expect the profile counters for garbage collected functions to also +// be garbage collected. + +// PRF_CNTS: Hex dump of section '__llvm_prf_cnts': +// PRF_CNTS-NEXT: {{.*}} 00000000 00000000 {{.*$}} + +// PGO: Counters: +// PGO-NEXT: main: +// PGO-NEXT: Hash: +// PGO-NEXT: Counters: 1 +// PGO-NEXT: Instrumentation level: IR +// PGO-NEXT: Functions shown: 1 +// PGO-NEXT: Total functions: 1 +// PGO-NEXT: Maximum function count: +// PGO-NEXT: Maximum internal block count: diff --git a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp index ceceafa860300b..06ccf439501aff 100644 --- a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp +++ b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp @@ -844,6 +844,8 @@ InstrProfiling::getOrCreateRegionCounters(InstrProfIncrementInst *Inc) { CounterPtr->setAlignment(Align(8)); MaybeSetComdat(CounterPtr); CounterPtr->setLinkage(Linkage); + CounterPtr->setMetadata(LLVMContext::MD_associated, + MDNode::get(Ctx, ValueAsMetadata::get(CounterPtr))); auto *Int8PtrTy = Type::getInt8PtrTy(Ctx); // Allocate statically the array of pointers to value profile nodes for @@ -865,6 +867,8 @@ InstrProfiling::getOrCreateRegionCounters(InstrProfIncrementInst *Inc) { getInstrProfSectionName(IPSK_vals, TT.getObjectFormat())); ValuesVar->setAlignment(Align(8)); MaybeSetComdat(ValuesVar); + ValuesVar->setMetadata(LLVMContext::MD_associated, + MDNode::get(Ctx, ValueAsMetadata::get(CounterPtr))); ValuesPtrExpr = ConstantExpr::getBitCast(ValuesVar, Type::getInt8PtrTy(Ctx)); } @@ -899,6 +903,8 @@ InstrProfiling::getOrCreateRegionCounters(InstrProfIncrementInst *Inc) { Data->setAlignment(Align(INSTR_PROF_DATA_ALIGNMENT)); MaybeSetComdat(Data); Data->setLinkage(Linkage); + Data->setMetadata(LLVMContext::MD_associated, + MDNode::get(Ctx, ValueAsMetadata::get(CounterPtr))); PD.RegionCounters = CounterPtr; PD.DataVar = Data; diff --git a/llvm/test/Instrumentation/InstrProfiling/associated.ll b/llvm/test/Instrumentation/InstrProfiling/associated.ll new file mode 100644 index 00000000000000..dbb496dd8bdbe9 --- /dev/null +++ b/llvm/test/Instrumentation/InstrProfiling/associated.ll @@ -0,0 +1,16 @@ +; RUN: opt < %s -instrprof -S | FileCheck %s +; RUN: opt < %s -passes=instrprof -S | FileCheck %s + +@__profn_foo = hidden constant [3 x i8] c"foo" + +; CHECK: @__profc_foo = hidden global [1 x i64] zeroinitializer, section "__llvm_prf_cnts", align 8, !associated !0 +; CHECK: @__profd_foo = hidden global {{.*}}, section "__llvm_prf_data", align 8, !associated !0 + +define void @foo() { + call void @llvm.instrprof.increment(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @__profn_foo, i32 0, i32 0), i64 0, i32 1, i32 0) + ret void +} + +declare void @llvm.instrprof.increment(i8*, i64, i32, i32) + +; CHECK: !0 = !{[1 x i64]* @__profc_foo} diff --git a/llvm/test/Transforms/PGOProfile/associated.ll b/llvm/test/Transforms/PGOProfile/associated.ll new file mode 100644 index 00000000000000..2d12d19d05ed30 --- /dev/null +++ b/llvm/test/Transforms/PGOProfile/associated.ll @@ -0,0 +1,11 @@ +; RUN: opt < %s -pgo-instr-gen -instrprof -S | FileCheck %s +; RUN: opt < %s -passes=pgo-instr-gen,instrprof -S | FileCheck %s + +; CHECK: @__profc_foo = private global [1 x i64] zeroinitializer, section "__llvm_prf_cnts", align 8, !associated !0 +; CHECK: @__profd_foo = private global {{.*}}, section "__llvm_prf_data", align 8, !associated !0 + +define void @foo() { + ret void +} + +; CHECK: !0 = !{[1 x i64]* @__profc_foo} diff --git a/llvm/test/Transforms/PGOProfile/counter_promo.ll b/llvm/test/Transforms/PGOProfile/counter_promo.ll index 812d0fefaa79ed..da08ace8493302 100644 --- a/llvm/test/Transforms/PGOProfile/counter_promo.ll +++ b/llvm/test/Transforms/PGOProfile/counter_promo.ll @@ -60,7 +60,7 @@ bb12: ; preds = %bb9 ; ATOMIC_PROMO: atomicrmw add {{.*}} @__profc_foo{{.*}}0), i64 %[[LIVEOUT1]] seq_cst ; ATOMIC_PROMO-NEXT: atomicrmw add {{.*}} @__profc_foo{{.*}}1), i64 %[[LIVEOUT2]] seq_cst ; ATOMIC_PROMO-NEXT: atomicrmw add {{.*}} @__profc_foo{{.*}}2), i64 %[[LIVEOUT3]] seq_cst -; PROMO-NOT: @__profc_foo +; PROMO-NOT: @__profc_foo{{.*}}) } diff --git a/llvm/test/Transforms/PGOProfile/counter_promo_mexits.ll b/llvm/test/Transforms/PGOProfile/counter_promo_mexits.ll index bb799757a47cc2..8e907acb34da15 100644 --- a/llvm/test/Transforms/PGOProfile/counter_promo_mexits.ll +++ b/llvm/test/Transforms/PGOProfile/counter_promo_mexits.ll @@ -69,7 +69,7 @@ bb15_0: ; preds = %bb11 ; PROMO-NEXT: %pgocount{{.*}} = load {{.*}} @__profc_foo{{.*}} 4) ; PROMO-NEXT: add ; PROMO-NEXT: store {{.*}}@__profc_foo{{.*}}4) -; PROMO-NOT: @__profc_foo +; PROMO-NOT: @__profc_foo{{.*}}) bb15: ; preds = %bb14, %bb4