[clang] [compiler-rt] [llvm] [PGO] Initialize GOV Writeout and Reset Functions in the Runtime on AIX (PR #108570)

via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 13 07:36:55 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-powerpc

Author: Qiongsi Wu (qiongsiwu)

<details>
<summary>Changes</summary>

This PR registers the writeout and reset functions for `gcov` for all modules in the PGO runtime, instead of registering them
using global constructors in each module. The change is made for AIX only, but the same mechanism works on Linux on Power.

When registering such functions using global constructors in each module without `-ffunction-sections`, the AIX linker cannot garbage collect unused undefined symbols, because such symbols are grouped in the same section as the `__sinit` symbol. Keeping such undefined symbols causes link errors. This PR implements the initialization in the runtime, hence avoiding introducing `__sinit` into each module. 

The implementation adds a new global variable `__llvm_covinit_functions` to each module. This new global variable contains the function pointers to the `Writeout` and `Reset` functions. `__llvm_covinit_functions`'s section is the named section `__llvm_covinit`. The linker will aggregate all the `__llvm_covinit` sections from each module
to form one single named section in the final binary. The pair of functions
```
const __llvm_gcov_init_func_struct *__llvm_profile_begin_covinit();
const __llvm_gcov_init_func_struct *__llvm_profile_end_covinit();
```
are implemented to return the start and end address of this named section in the final binary, and they are used in function
```
__llvm_profile_gcov_initialize()
```
(which is a constructor function in the runtime) so the runtime knows the addresses of all the `Writeout` and `Reset` functions from all the modules. 

One noticeable implementation detail relevant to AIX is that to preserve the `__llvm_covinit` from the linker's garbage collection, a `.ref` pseudo instruction is inserted into them, referring to the section that contains the `__llvm_gcov_ctr` variables, which are used in the instrumented code. The `__llvm_gcov_ctr` variables did not belong to named sections before, but this PR added them to the `__llvm_gcov_ctr_section` named section, so we can add a `.ref` pseudo instruction that refers to them in the `__llvm_covinit` section. 

---

Patch is 26.57 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/108570.diff


14 Files Affected:

- (modified) clang/test/CodeGen/attr-function-return.c (+1-1) 
- (modified) clang/test/CodeGen/code-coverage.c (+16-7) 
- (modified) compiler-rt/include/profile/InstrProfData.inc (+22) 
- (modified) compiler-rt/lib/profile/GCDAProfiling.c (+19) 
- (modified) compiler-rt/lib/profile/InstrProfiling.h (+11) 
- (modified) compiler-rt/lib/profile/InstrProfilingPlatformAIX.c (+4-1) 
- (modified) compiler-rt/lib/profile/InstrProfilingPlatformLinux.c (+16) 
- (added) compiler-rt/test/profile/AIX/gcov-undef-sym.test (+52) 
- (modified) llvm/include/llvm/ProfileData/InstrProfData.inc (+22) 
- (modified) llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp (+23) 
- (modified) llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp (+44-1) 
- (modified) llvm/test/Transforms/GCOVProfiling/kcfi-normalize.ll (+10-4) 
- (modified) llvm/test/Transforms/GCOVProfiling/kcfi.ll (+10-4) 
- (modified) llvm/test/Transforms/GCOVProfiling/module-flags.ll (+9-3) 


``````````diff
diff --git a/clang/test/CodeGen/attr-function-return.c b/clang/test/CodeGen/attr-function-return.c
index df2cabf28693a3..1aca3b1bfa5b59 100644
--- a/clang/test/CodeGen/attr-function-return.c
+++ b/clang/test/CodeGen/attr-function-return.c
@@ -9,7 +9,7 @@
 // RUN:   | FileCheck %s --check-prefixes=CHECK,CHECK-EXTERN
 // RUN: %clang_cc1 -std=gnu2x -triple x86_64-linux-gnu %s -emit-llvm -o - \
 // RUN:  -mfunction-return=thunk-extern -coverage-data-file=/dev/null \
-// RUN:   | FileCheck %s --check-prefix=CHECK-GCOV
+// RUN:   | FileCheck %s --check-prefixes=CHECK-GCOV
 // RUN: %clang_cc1 -std=gnu2x -triple x86_64-linux-gnu %s -emit-llvm -o - \
 // RUN:  -mfunction-return=thunk-extern -fsanitize=address \
 // RUN:   | FileCheck %s --check-prefix=CHECK-ASAN
diff --git a/clang/test/CodeGen/code-coverage.c b/clang/test/CodeGen/code-coverage.c
index d7994bab35d81a..30c59d58380def 100644
--- a/clang/test/CodeGen/code-coverage.c
+++ b/clang/test/CodeGen/code-coverage.c
@@ -3,12 +3,18 @@
 /// 4.7 enables cfg_checksum.
 /// 4.8 (default, compatible with gcov 7) emits the exit block the second.
 // RUN: rm -rf %t && mkdir %t && cd %t
-// RUN: %clang_cc1 -emit-llvm -disable-red-zone -coverage-data-file=/dev/null -coverage-version='304*' %s -o - | \
-// RUN:   FileCheck --check-prefixes=CHECK,304 %s
-// RUN: %clang_cc1 -emit-llvm -disable-red-zone -coverage-data-file=/dev/null -coverage-version='407*' %s -o - | \
-// RUN:   FileCheck --check-prefixes=CHECK,407 %s
-// RUN: %clang_cc1 -emit-llvm -disable-red-zone -coverage-data-file=/dev/null %s -o - | \
-// RUN:   FileCheck --check-prefixes=CHECK,408 %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -disable-red-zone -coverage-data-file=/dev/null -coverage-version='304*' %s -o - | \
+// RUN:   FileCheck --check-prefixes=CHECK,CHECK-ELF,304 %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -disable-red-zone -coverage-data-file=/dev/null -coverage-version='407*' %s -o - | \
+// RUN:   FileCheck --check-prefixes=CHECK,CHECK-ELF,407 %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -disable-red-zone -coverage-data-file=/dev/null %s -o - | \
+// RUN:   FileCheck --check-prefixes=CHECK,CHECK-ELF,408 %s
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -emit-llvm -disable-red-zone -coverage-data-file=/dev/null -coverage-version='304*' %s -o - | \
+// RUN:   FileCheck --check-prefixes=CHECK,CHECK-XCOFF,304 %s
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -emit-llvm -disable-red-zone -coverage-data-file=/dev/null -coverage-version='407*' %s -o - | \
+// RUN:   FileCheck --check-prefixes=CHECK,CHECK-XCOFF,407 %s
+// RUN: %clang_cc1 -triple powerpc64-ibm-aix -emit-llvm -disable-red-zone -coverage-data-file=/dev/null %s -o - | \
+// RUN:   FileCheck --check-prefixes=CHECK,CHECK-XCOFF,408 %s
 
 // RUN: %clang_cc1 -emit-llvm -disable-red-zone -coverage-notes-file=aaa.gcno -coverage-data-file=bbb.gcda -debug-info-kind=limited -dwarf-version=4 %s -o - | FileCheck %s --check-prefix GCOV_FILE_INFO
 
@@ -23,6 +29,9 @@
 // NEWPM-O3: Running pass: ForceFunctionAttrsPass
 // NEWPM-O3: Running pass: GCOVProfilerPass
 
+// Check for gcov initialization function pointers.
+// CHECK-XCOFF: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
+
 int test1(int a) {
   switch (a % 2) {
   case 0:
@@ -52,7 +61,7 @@ int test2(int b) {
 // Check that the noredzone flag is set on the generated functions.
 
 // CHECK: void @__llvm_gcov_writeout() unnamed_addr [[NRZ:#[0-9]+]]
-// CHECK: void @__llvm_gcov_init() unnamed_addr [[NRZ]]
+// CHECK-ELF: void @__llvm_gcov_init() unnamed_addr [[NRZ]]
 
 // CHECK: attributes [[NRZ]] = { {{.*}}noredzone{{.*}} }
 
diff --git a/compiler-rt/include/profile/InstrProfData.inc b/compiler-rt/include/profile/InstrProfData.inc
index b9df3266fbcf8f..591fc1401e1aab 100644
--- a/compiler-rt/include/profile/InstrProfData.inc
+++ b/compiler-rt/include/profile/InstrProfData.inc
@@ -303,6 +303,18 @@ COVMAP_HEADER(uint32_t, Int32Ty, Version, \
 #undef COVMAP_HEADER
 /* COVMAP_HEADER end.  */
 
+/* COVINIT_FUNC start */
+#ifndef COVINIT_FUNC
+#define COVINIT_FUNC(Type, LLVMType, Name, Initializer)
+#else
+#define INSTR_PROF_DATA_DEFINED
+#endif
+COVINIT_FUNC(IntPtrT, llvm::PointerType::getUnqual(Ctx), WriteoutFunction, \
+             WriteoutF)
+COVINIT_FUNC(IntPtrT, llvm::PointerType::getUnqual(Ctx), ResetFunction, \
+             ResetF)
+#undef COVINIT_FUNC
+/* COVINIT_FUNC end */
 
 #ifdef INSTR_PROF_SECT_ENTRY
 #define INSTR_PROF_DATA_DEFINED
@@ -345,6 +357,9 @@ INSTR_PROF_SECT_ENTRY(IPSK_covdata, \
 INSTR_PROF_SECT_ENTRY(IPSK_covname, \
                       INSTR_PROF_QUOTE(INSTR_PROF_COVNAME_COMMON), \
                       INSTR_PROF_COVNAME_COFF, "__LLVM_COV,")
+INSTR_PROF_SECT_ENTRY(IPSK_covinit, \
+                      INSTR_PROF_QUOTE(INSTR_PROF_COVINIT_COMMON), \
+                      INSTR_PROF_COVINIT_COFF, "__LLVM_COV,")
 
 #undef INSTR_PROF_SECT_ENTRY
 #endif
@@ -761,6 +776,8 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
 #define INSTR_PROF_COVDATA_COMMON __llvm_covdata
 #define INSTR_PROF_COVNAME_COMMON __llvm_covnames
 #define INSTR_PROF_ORDERFILE_COMMON __llvm_orderfile
+#define INSTR_PROF_COVINIT_COMMON __llvm_covinit
+
 /* Windows section names. Because these section names contain dollar characters,
  * they must be quoted.
  */
@@ -781,6 +798,9 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
 #define INSTR_PROF_COVNAME_COFF ".lcovn"
 #define INSTR_PROF_ORDERFILE_COFF ".lorderfile$M"
 
+// TODO: Placeholder for Windows. We need to revise when we upstream this.
+#define INSTR_PROF_COVINIT_COFF ".lcovd$M"
+
 #ifdef _WIN32
 /* Runtime section names and name strings.  */
 #define INSTR_PROF_DATA_SECT_NAME INSTR_PROF_DATA_COFF
@@ -800,6 +820,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
 #define INSTR_PROF_COVDATA_SECT_NAME INSTR_PROF_COVDATA_COFF
 #define INSTR_PROF_COVNAME_SECT_NAME INSTR_PROF_COVNAME_COFF
 #define INSTR_PROF_ORDERFILE_SECT_NAME INSTR_PROF_ORDERFILE_COFF
+#define INSTR_PROF_COVINIT_SECT_NAME INSTR_PROF_COVINIT_COFF
 #else
 /* Runtime section names and name strings.  */
 #define INSTR_PROF_DATA_SECT_NAME INSTR_PROF_QUOTE(INSTR_PROF_DATA_COMMON)
@@ -821,6 +842,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
 /* Order file instrumentation. */
 #define INSTR_PROF_ORDERFILE_SECT_NAME                                         \
   INSTR_PROF_QUOTE(INSTR_PROF_ORDERFILE_COMMON)
+#define INSTR_PROF_COVINIT_SECT_NAME INSTR_PROF_QUOTE(INSTR_PROF_COVINIT_COMMON)
 #endif
 
 #define INSTR_PROF_ORDERFILE_BUFFER_NAME _llvm_order_file_buffer
diff --git a/compiler-rt/lib/profile/GCDAProfiling.c b/compiler-rt/lib/profile/GCDAProfiling.c
index d6e2175169e4a5..452a62fcc9d21e 100644
--- a/compiler-rt/lib/profile/GCDAProfiling.c
+++ b/compiler-rt/lib/profile/GCDAProfiling.c
@@ -624,6 +624,25 @@ void llvm_gcov_init(fn_ptr wfn, fn_ptr rfn) {
   }
 }
 
+#if defined(_AIX)
+COMPILER_RT_VISIBILITY __attribute__((constructor)) void
+__llvm_profile_gcov_initialize() {
+  const __llvm_gcov_init_func_struct *InitFuncStart =
+      __llvm_profile_begin_covinit();
+  const __llvm_gcov_init_func_struct *InitFuncEnd =
+      __llvm_profile_end_covinit();
+
+  for (const __llvm_gcov_init_func_struct *Ptr = InitFuncStart;
+       Ptr != InitFuncEnd; ++Ptr) {
+    fn_ptr wfn = (fn_ptr)Ptr->WriteoutFunction;
+    fn_ptr rfn = (fn_ptr)Ptr->ResetFunction;
+    if (!(wfn && rfn))
+      continue;
+    llvm_gcov_init(wfn, rfn);
+  }
+}
+#endif
+
 void __gcov_dump(void) {
   for (struct fn_node *f = writeout_fn_list.head; f; f = f->next)
     f->fn();
diff --git a/compiler-rt/lib/profile/InstrProfiling.h b/compiler-rt/lib/profile/InstrProfiling.h
index 6906d52eacaf1b..a62eca738327a9 100644
--- a/compiler-rt/lib/profile/InstrProfiling.h
+++ b/compiler-rt/lib/profile/InstrProfiling.h
@@ -54,6 +54,12 @@ typedef struct COMPILER_RT_ALIGNAS(INSTR_PROF_DATA_ALIGNMENT) VTableProfData {
 #include "profile/InstrProfData.inc"
 } VTableProfData;
 
+typedef struct COMPILER_RT_ALIGNAS(INSTR_PROF_DATA_ALIGNMENT)
+    __llvm_gcov_init_func_struct {
+#define COVINIT_FUNC(Type, LLVMType, Name, Initializer) Type Name;
+#include "profile/InstrProfData.inc"
+} __llvm_gcov_init_func_struct;
+
 /*!
  * \brief Return 1 if profile counters are continuously synced to the raw
  * profile via an mmap(). This is in contrast to the default mode, in which
@@ -208,6 +214,9 @@ void __llvm_profile_initialize_file(void);
 /*! \brief Initialize the profile runtime. */
 void __llvm_profile_initialize(void);
 
+/*! \brief Initialize the gcov profile runtime. */
+void __llvm_profile_gcov_initialize(void);
+
 /*!
  * \brief Return path prefix (excluding the base filename) of the profile data.
  * This is useful for users using \c -fprofile-generate=./path_prefix who do
@@ -324,4 +333,6 @@ COMPILER_RT_VISIBILITY extern uint64_t
  */
 extern char INSTR_PROF_PROFILE_NAME_VAR[1]; /* __llvm_profile_filename. */
 
+const __llvm_gcov_init_func_struct *__llvm_profile_begin_covinit();
+const __llvm_gcov_init_func_struct *__llvm_profile_end_covinit();
 #endif /* PROFILE_INSTRPROFILING_H_ */
diff --git a/compiler-rt/lib/profile/InstrProfilingPlatformAIX.c b/compiler-rt/lib/profile/InstrProfilingPlatformAIX.c
index b9d51b698b414f..651f8785d0b940 100644
--- a/compiler-rt/lib/profile/InstrProfilingPlatformAIX.c
+++ b/compiler-rt/lib/profile/InstrProfilingPlatformAIX.c
@@ -202,6 +202,8 @@ static int dummy_vname[0] COMPILER_RT_SECTION(
     COMPILER_RT_SEG INSTR_PROF_VNAME_SECT_NAME);
 static int dummy_vtab[0] COMPILER_RT_SECTION(
     COMPILER_RT_SEG INSTR_PROF_VTAB_SECT_NAME);
+static int dummy_covinit_funcs[0] COMPILER_RT_SECTION(
+    COMPILER_RT_SEG INSTR_PROF_COVINIT_SECT_NAME);
 
 // To avoid GC'ing of the dummy variables by the linker, reference them in an
 // array and reference the array in the runtime registration code
@@ -214,7 +216,8 @@ COMPILER_RT_VISIBILITY
 void *__llvm_profile_keep[] = {(void *)&dummy_cnts,  (void *)&dummy_bits,
                                (void *)&dummy_data,  (void *)&dummy_name,
                                (void *)&dummy_vnds,  (void *)&dummy_orderfile,
-                               (void *)&dummy_vname, (void *)&dummy_vtab};
+                               (void *)&dummy_vname, (void *)&dummy_vtab,
+                               (void *)&dummy_covinit_funcs};
 #ifdef __GNUC__
 #pragma GCC diagnostic pop
 #endif
diff --git a/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c b/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c
index b766436497b741..4c22bf42eac569 100644
--- a/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c
+++ b/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c
@@ -35,6 +35,8 @@
 #define PROF_ORDERFILE_START INSTR_PROF_SECT_START(INSTR_PROF_ORDERFILE_COMMON)
 #define PROF_VNODES_START INSTR_PROF_SECT_START(INSTR_PROF_VNODES_COMMON)
 #define PROF_VNODES_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_VNODES_COMMON)
+#define PROF_COVINIT_START INSTR_PROF_SECT_START(INSTR_PROF_COVINIT_COMMON)
+#define PROF_COVINIT_STOP INSTR_PROF_SECT_STOP(INSTR_PROF_COVINIT_COMMON)
 
 /* Declare section start and stop symbols for various sections
  * generated by compiler instrumentation.
@@ -56,6 +58,10 @@ 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;
+extern __llvm_gcov_init_func_struct PROF_COVINIT_START COMPILER_RT_VISIBILITY
+    COMPILER_RT_WEAK;
+extern __llvm_gcov_init_func_struct PROF_COVINIT_STOP COMPILER_RT_VISIBILITY
+    COMPILER_RT_WEAK;
 
 COMPILER_RT_VISIBILITY const __llvm_profile_data *
 __llvm_profile_begin_data(void) {
@@ -110,6 +116,16 @@ COMPILER_RT_VISIBILITY ValueProfNode *__llvm_profile_end_vnodes(void) {
 COMPILER_RT_VISIBILITY ValueProfNode *CurrentVNode = &PROF_VNODES_START;
 COMPILER_RT_VISIBILITY ValueProfNode *EndVNode = &PROF_VNODES_STOP;
 
+COMPILER_RT_VISIBILITY const __llvm_gcov_init_func_struct *
+__llvm_profile_begin_covinit() {
+  return &PROF_COVINIT_START;
+}
+
+COMPILER_RT_VISIBILITY const __llvm_gcov_init_func_struct *
+__llvm_profile_end_covinit() {
+  return &PROF_COVINIT_STOP;
+}
+
 #ifdef NT_GNU_BUILD_ID
 static size_t RoundUp(size_t size, size_t align) {
   return (size + align - 1) & ~(align - 1);
diff --git a/compiler-rt/test/profile/AIX/gcov-undef-sym.test b/compiler-rt/test/profile/AIX/gcov-undef-sym.test
new file mode 100644
index 00000000000000..e377b321ca66cd
--- /dev/null
+++ b/compiler-rt/test/profile/AIX/gcov-undef-sym.test
@@ -0,0 +1,52 @@
+// The undefined symbol should not cause link errors, and we should
+// obtain the expected coverage report.
+
+// Test the --coverage option.
+RUN: rm -rf %t0 && split-file %s %t0 && cd %t0
+RUN: %clang bar.c main.c undef.c --coverage -c
+RUN: ar -X32_64 -rv libfoo.a undef.o bar.o
+RUN: %clang main.o -L. -lfoo --coverage -o main.exe
+RUN: main.exe
+RUN: llvm-cov gcov -t main.gcda |  FileCheck --check-prefix=MAIN %s
+RUN: llvm-cov gcov -t bar.gcda |  FileCheck --check-prefix=BAR %s
+
+// Test the pgogen -fprofile-arcs -ftest-coverage option combination.
+RUN: rm -rf %t1 && split-file %s %t1 && cd %t1
+RUN: %clang_pgogen bar.c main.c undef.c -fprofile-arcs -ftest-coverage -c
+RUN: ar -X32_64 -rv libfoo.a undef.o bar.o
+RUN: %clang_pgogen main.o -L. -lfoo -fprofile-generate -fprofile-arcs -ftest-coverage -o main.exe
+RUN: main.exe
+RUN: llvm-cov gcov -t main.gcda |  FileCheck --check-prefix=MAIN %s
+RUN: llvm-cov gcov -t bar.gcda |  FileCheck --check-prefix=BAR %s
+
+// Test the pgogen -Wl,-bcdtors:mbr option combination.
+RUN: rm -rf %t2 && split-file %s %t2 && cd %t2
+RUN: %clang_pgogen bar.c main.c undef.c -fprofile-arcs -ftest-coverage -c
+RUN: ar -X32_64 -rv libfoo.a undef.o bar.o
+RUN: %clang_pgogen main.o -L. -lfoo -fprofile-generate -fprofile-arcs -ftest-coverage -Wl,-bcdtors:mbr -o main.exe
+RUN: main.exe
+RUN: llvm-cov gcov -t main.gcda |  FileCheck --check-prefix=MAIN %s
+RUN: llvm-cov gcov -t bar.gcda |  FileCheck --check-prefix=BAR %s
+
+MAIN:        1:    2:int main() {
+MAIN:        1:    3:  return bar();
+BAR:         1:    1:int bar() {
+BAR:         1:    2:  return 0;
+
+//--- main.c
+int bar();
+int main() {
+  return bar();
+}
+
+
+//--- bar.c
+int bar() {
+  return 0;
+}
+
+//--- undef.c
+void undef_func();
+void foo() {
+  undef_func();
+}
diff --git a/llvm/include/llvm/ProfileData/InstrProfData.inc b/llvm/include/llvm/ProfileData/InstrProfData.inc
index b9df3266fbcf8f..591fc1401e1aab 100644
--- a/llvm/include/llvm/ProfileData/InstrProfData.inc
+++ b/llvm/include/llvm/ProfileData/InstrProfData.inc
@@ -303,6 +303,18 @@ COVMAP_HEADER(uint32_t, Int32Ty, Version, \
 #undef COVMAP_HEADER
 /* COVMAP_HEADER end.  */
 
+/* COVINIT_FUNC start */
+#ifndef COVINIT_FUNC
+#define COVINIT_FUNC(Type, LLVMType, Name, Initializer)
+#else
+#define INSTR_PROF_DATA_DEFINED
+#endif
+COVINIT_FUNC(IntPtrT, llvm::PointerType::getUnqual(Ctx), WriteoutFunction, \
+             WriteoutF)
+COVINIT_FUNC(IntPtrT, llvm::PointerType::getUnqual(Ctx), ResetFunction, \
+             ResetF)
+#undef COVINIT_FUNC
+/* COVINIT_FUNC end */
 
 #ifdef INSTR_PROF_SECT_ENTRY
 #define INSTR_PROF_DATA_DEFINED
@@ -345,6 +357,9 @@ INSTR_PROF_SECT_ENTRY(IPSK_covdata, \
 INSTR_PROF_SECT_ENTRY(IPSK_covname, \
                       INSTR_PROF_QUOTE(INSTR_PROF_COVNAME_COMMON), \
                       INSTR_PROF_COVNAME_COFF, "__LLVM_COV,")
+INSTR_PROF_SECT_ENTRY(IPSK_covinit, \
+                      INSTR_PROF_QUOTE(INSTR_PROF_COVINIT_COMMON), \
+                      INSTR_PROF_COVINIT_COFF, "__LLVM_COV,")
 
 #undef INSTR_PROF_SECT_ENTRY
 #endif
@@ -761,6 +776,8 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
 #define INSTR_PROF_COVDATA_COMMON __llvm_covdata
 #define INSTR_PROF_COVNAME_COMMON __llvm_covnames
 #define INSTR_PROF_ORDERFILE_COMMON __llvm_orderfile
+#define INSTR_PROF_COVINIT_COMMON __llvm_covinit
+
 /* Windows section names. Because these section names contain dollar characters,
  * they must be quoted.
  */
@@ -781,6 +798,9 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
 #define INSTR_PROF_COVNAME_COFF ".lcovn"
 #define INSTR_PROF_ORDERFILE_COFF ".lorderfile$M"
 
+// TODO: Placeholder for Windows. We need to revise when we upstream this.
+#define INSTR_PROF_COVINIT_COFF ".lcovd$M"
+
 #ifdef _WIN32
 /* Runtime section names and name strings.  */
 #define INSTR_PROF_DATA_SECT_NAME INSTR_PROF_DATA_COFF
@@ -800,6 +820,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
 #define INSTR_PROF_COVDATA_SECT_NAME INSTR_PROF_COVDATA_COFF
 #define INSTR_PROF_COVNAME_SECT_NAME INSTR_PROF_COVNAME_COFF
 #define INSTR_PROF_ORDERFILE_SECT_NAME INSTR_PROF_ORDERFILE_COFF
+#define INSTR_PROF_COVINIT_SECT_NAME INSTR_PROF_COVINIT_COFF
 #else
 /* Runtime section names and name strings.  */
 #define INSTR_PROF_DATA_SECT_NAME INSTR_PROF_QUOTE(INSTR_PROF_DATA_COMMON)
@@ -821,6 +842,7 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
 /* Order file instrumentation. */
 #define INSTR_PROF_ORDERFILE_SECT_NAME                                         \
   INSTR_PROF_QUOTE(INSTR_PROF_ORDERFILE_COMMON)
+#define INSTR_PROF_COVINIT_SECT_NAME INSTR_PROF_QUOTE(INSTR_PROF_COVINIT_COMMON)
 #endif
 
 #define INSTR_PROF_ORDERFILE_BUFFER_NAME _llvm_order_file_buffer
diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index dcde86388dcd9d..3251340aa67a6a 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -290,6 +290,8 @@ class PPCAIXAsmPrinter : public PPCAsmPrinter {
 
   void emitPGORefs(Module &M);
 
+  void emitGCOVRefs();
+
   void emitEndOfAsmFile(Module &) override;
 
   void emitLinkage(const GlobalValue *GV, MCSymbol *GVSym) const override;
@@ -2962,6 +2964,26 @@ void PPCAIXAsmPrinter::emitPGORefs(Module &M) {
   }
 }
 
+void PPCAIXAsmPrinter::emitGCOVRefs() {
+  if (!OutContext.hasXCOFFSection(
+          "__llvm_gcov_ctr_section",
+          XCOFF::CsectProperties(XCOFF::XMC_RW, XCOFF::XTY_SD)))
+    return;
+
+  MCSection *CtrSection = OutContext.getXCOFFSection(
+      "__llvm_gcov_ctr_section", SectionKind::getData(),
+      XCOFF::CsectProperties(XCOFF::XMC_RW, XCOFF::XTY_SD),
+      /*MultiSymbolsAllowed*/ true);
+
+  OutStreamer->switchSection(CtrSection);
+  if (OutContext.hasXCOFFSection(
+          "__llvm_covinit",
+          XCOFF::CsectProperties(XCOFF::XMC_RW, XCOFF::XTY_SD))) {
+    MCSymbol *S = OutContext.getOrCreateSymbol("__llvm_covinit[RW]");
+    OutStreamer->emitXCOFFRefDirective(S);
+  }
+}
+
 void PPCAIXAsmPrinter::emitEndOfAsmFile(Module &M) {
   // If there are no functions and there are no toc-data definitions in this
   // module, we will never need to reference the TOC base.
@@ -2969,6 +2991,7 @@ void PPCAIXAsmPrinter::emitEndOfAsmFile(Module &M) {
     return;
 
   emitPGORefs(M);
+  emitGCOVRefs();
 
   // Switch to section to emit TOC base.
   OutStreamer->switchSection(getObjFileLowering().getTOCBaseSection());
diff --git a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
index c7f6f2a43c17f5..4a399ad438215a 100644
--- a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
+++ b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
@@ -29,6 +29,7 @@
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/IntrinsicInst.h"
 #include "llvm/IR/Module.h"
+#include "llvm/ProfileData/InstrProf.h"
 #include "llvm/Support/CRC.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
@@ -121,8 +122,11 @@ class GCOVProfiler {
 
   Function *createInternalFunction(FunctionType *FTy, StringRef Name,
                                    StringRef MangledType = "");
+
   void emitGlobalConstructor(
       SmallVectorImpl<std::pair<GlobalVariable *, MDNode *>> &CountersBySP);
+  void emitModuleInitFunctionPtrs(
+      SmallVectorImpl<std::pair<GlobalVariable *, MDNode *>> &CountersBySP);
 
   bool isFunctionInstrumented(const Function &F);
   std::vector<Regex> createRegexesFromString(StringRef RegexesStr);
@@ -914,6 +918,7 @@ bool GCOVProfiler::emitProfileNotes(
         GlobalVariable *Counters = new GlobalVariable(
             *M, Co...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/108570


More information about the cfe-commits mailing list