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

Qiongsi Wu via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 15 08:16:09 PDT 2024


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

>From e1b5e886f3f7642ec691a08378a1f2cdc9e2465f Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <qwu at ibm.com>
Date: Wed, 11 Sep 2024 14:56:58 -0400
Subject: [PATCH 1/7] Initial commit.

---
 clang/test/CodeGen/attr-function-return.c     |  5 +-
 clang/test/CodeGen/code-coverage.c            |  5 +-
 clang/test/CodeGen/coverage-target-attr.c     |  5 +-
 compiler-rt/include/profile/InstrProfData.inc | 22 ++++++++
 compiler-rt/lib/profile/GCDAProfiling.c       | 17 ++++++
 compiler-rt/lib/profile/InstrProfiling.h      | 11 ++++
 .../lib/profile/InstrProfilingPlatformAIX.c   |  5 +-
 .../lib/profile/InstrProfilingPlatformLinux.c | 16 ++++++
 .../test/profile/AIX/gcov-undef-sym.test      | 52 +++++++++++++++++++
 .../llvm/ProfileData/InstrProfData.inc        | 22 ++++++++
 llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp     | 23 ++++++++
 .../Instrumentation/GCOVProfiling.cpp         | 48 ++++++++++-------
 .../GCOVProfiling/kcfi-normalize.ll           |  7 +--
 llvm/test/Transforms/GCOVProfiling/kcfi.ll    |  7 +--
 .../Transforms/GCOVProfiling/module-flags.ll  |  4 +-
 15 files changed, 218 insertions(+), 31 deletions(-)
 create mode 100644 compiler-rt/test/profile/AIX/gcov-undef-sym.test

diff --git a/clang/test/CodeGen/attr-function-return.c b/clang/test/CodeGen/attr-function-return.c
index df2cabf28693a3..9c5cbca5640843 100644
--- a/clang/test/CodeGen/attr-function-return.c
+++ b/clang/test/CodeGen/attr-function-return.c
@@ -17,6 +17,9 @@
 // RUN:  -mfunction-return=thunk-extern -fsanitize=thread \
 // RUN:   | FileCheck %s --check-prefix=CHECK-TSAN
 
+// Check for gcov initialization function pointers.
+// CHECK-GCOV: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
+
 #if !__has_attribute(function_return)
 #error "missing attribute support for function_return"
 #endif
@@ -104,7 +107,7 @@ void no_attrs(void) {}
 // Test synthetic functions.
 // CHECK-GCOV: @__llvm_gcov_writeout() unnamed_addr [[EXTERNGCOV:#[0-9]+]]
 // CHECK-GCOV: @__llvm_gcov_reset() unnamed_addr [[EXTERNGCOV]]
-// CHECK-GCOV: @__llvm_gcov_init() unnamed_addr [[EXTERNGCOV]]
+// CHECK-GCOV-NOT: @__llvm_gcov_init() unnamed_addr [[EXTERNGCOV]]
 // CHECK-ASAN: @asan.module_ctor() [[EXTERNASAN:#[0-9]+]]
 // CHECK-TSAN: @tsan.module_ctor() [[EXTERNTSAN:#[0-9]+]]
 
diff --git a/clang/test/CodeGen/code-coverage.c b/clang/test/CodeGen/code-coverage.c
index d7994bab35d81a..4e3f0aba8c4098 100644
--- a/clang/test/CodeGen/code-coverage.c
+++ b/clang/test/CodeGen/code-coverage.c
@@ -49,10 +49,13 @@ int test2(int b) {
 /// 0x3430382a '4' '0' '8' '*'
 // 408-SAME: i32 875575338
 
+// Check for gcov initialization function pointers.
+// CHECK: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
+
 // 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-NOT: void @__llvm_gcov_init() unnamed_addr [[NRZ]]
 
 // CHECK: attributes [[NRZ]] = { {{.*}}noredzone{{.*}} }
 
diff --git a/clang/test/CodeGen/coverage-target-attr.c b/clang/test/CodeGen/coverage-target-attr.c
index d46299f5bee223..156e48ba6a31a2 100644
--- a/clang/test/CodeGen/coverage-target-attr.c
+++ b/clang/test/CodeGen/coverage-target-attr.c
@@ -1,9 +1,12 @@
 // RUN: %clang_cc1 -emit-llvm -coverage-notes-file=/dev/null -coverage-data-file=/dev/null -triple aarch64-linux-android30 -target-cpu generic -target-feature +tagged-globals -fsanitize=hwaddress %s -o %t
 // RUN: FileCheck %s < %t
 
+// Check for gcov initialization function pointers.
+// CHECK: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
+
 // CHECK: define internal void @__llvm_gcov_writeout() unnamed_addr [[ATTR:#[0-9]+]]
 // CHECK: define internal void @__llvm_gcov_reset() unnamed_addr [[ATTR]]
-// CHECK: define internal void @__llvm_gcov_init() unnamed_addr [[ATTR]]
+// CHECK-NOT: define internal void @__llvm_gcov_init() unnamed_addr [[ATTR]]
 // CHECK: define internal void @hwasan.module_ctor() [[ATTR2:#[0-9]+]]
 // CHECK: attributes [[ATTR]] = {{.*}} "target-cpu"="generic" "target-features"="+tagged-globals"
 // CHECK: attributes [[ATTR2]] = {{.*}} "target-cpu"="generic" "target-features"="+tagged-globals"
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..f430dd4e36ce37 100644
--- a/compiler-rt/lib/profile/GCDAProfiling.c
+++ b/compiler-rt/lib/profile/GCDAProfiling.c
@@ -624,6 +624,23 @@ void llvm_gcov_init(fn_ptr wfn, fn_ptr rfn) {
   }
 }
 
+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);
+  }
+}
+
 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..e6a7cdf3f80a9e 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,7 +122,7 @@ class GCOVProfiler {
 
   Function *createInternalFunction(FunctionType *FTy, StringRef Name,
                                    StringRef MangledType = "");
-  void emitGlobalConstructor(
+  void emitModuleInitFunctionPtrs(
       SmallVectorImpl<std::pair<GlobalVariable *, MDNode *>> &CountersBySP);
 
   bool isFunctionInstrumented(const Function &F);
@@ -914,6 +915,7 @@ bool GCOVProfiler::emitProfileNotes(
         GlobalVariable *Counters = new GlobalVariable(
             *M, CounterTy, false, GlobalValue::InternalLinkage,
             Constant::getNullValue(CounterTy), "__llvm_gcov_ctr");
+        Counters->setSection("__llvm_gcov_ctr_section");
         CountersBySP.emplace_back(Counters, SP);
 
         for (size_t I : llvm::seq<size_t>(0, Measured)) {
@@ -980,7 +982,7 @@ bool GCOVProfiler::emitProfileNotes(
     }
 
     if (EmitGCDA) {
-      emitGlobalConstructor(CountersBySP);
+      emitModuleInitFunctionPtrs(CountersBySP);
       EmitGCDA = false;
     }
   }
@@ -1001,32 +1003,38 @@ Function *GCOVProfiler::createInternalFunction(FunctionType *FTy,
   return F;
 }
 
-void GCOVProfiler::emitGlobalConstructor(
+void GCOVProfiler::emitModuleInitFunctionPtrs(
     SmallVectorImpl<std::pair<GlobalVariable *, MDNode *>> &CountersBySP) {
   Function *WriteoutF = insertCounterWriteout(CountersBySP);
   Function *ResetF = insertReset(CountersBySP);
 
-  // Create a small bit of code that registers the "__llvm_gcov_writeout" to
-  // be executed at exit and the "__llvm_gcov_reset" function to be executed
-  // when "__gcov_flush" is called.
-  FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), false);
-  Function *F = createInternalFunction(FTy, "__llvm_gcov_init", "_ZTSFvvE");
-  F->addFnAttr(Attribute::NoInline);
+  // Instead of creating a function call and add it to the constructors list,
+  // create a global variable in the __llvm_covinit section so the functions
+  // can be registered by a constructor in the runtime.
 
-  BasicBlock *BB = BasicBlock::Create(*Ctx, "entry", F);
-  IRBuilder<> Builder(BB);
+  auto &Ctx = M->getContext();
+
+  Type *InitFuncDataTy[] = {
+#define COVINIT_FUNC(Type, LLVMType, Name, Init) LLVMType,
+#include "llvm/ProfileData/InstrProfData.inc"
+  };
 
-  FTy = FunctionType::get(Type::getVoidTy(*Ctx), false);
-  auto *PFTy = PointerType::get(FTy, 0);
-  FTy = FunctionType::get(Builder.getVoidTy(), {PFTy, PFTy}, false);
+  auto STy = StructType::get(Ctx, ArrayRef(InitFuncDataTy));
 
-  // Initialize the environment and register the local writeout, flush and
-  // reset functions.
-  FunctionCallee GCOVInit = M->getOrInsertFunction("llvm_gcov_init", FTy);
-  Builder.CreateCall(GCOVInit, {WriteoutF, ResetF});
-  Builder.CreateRetVoid();
+  Constant *InitFuncPtrs[] = {
+#define COVINIT_FUNC(Type, LLVMType, Name, Init) Init,
+#include "llvm/ProfileData/InstrProfData.inc"
+  };
 
-  appendToGlobalCtors(*M, F, 0);
+  auto *CovInitGV =
+      new GlobalVariable(*M, STy, false, GlobalValue::PrivateLinkage, nullptr,
+                         "__llvm_covinit_functions");
+  CovInitGV->setInitializer(ConstantStruct::get(STy, InitFuncPtrs));
+  CovInitGV->setVisibility(GlobalValue::VisibilityTypes::DefaultVisibility);
+  CovInitGV->setSection(getInstrProfSectionName(
+      IPSK_covinit, Triple(M->getTargetTriple()).getObjectFormat()));
+  CovInitGV->setAlignment(Align(INSTR_PROF_DATA_ALIGNMENT));
+  CovInitGV->setConstant(true);
 }
 
 FunctionCallee GCOVProfiler::getStartFileFunc(const TargetLibraryInfo *TLI) {
diff --git a/llvm/test/Transforms/GCOVProfiling/kcfi-normalize.ll b/llvm/test/Transforms/GCOVProfiling/kcfi-normalize.ll
index 19122b920d1ca4..be53595e9201a0 100644
--- a/llvm/test/Transforms/GCOVProfiling/kcfi-normalize.ll
+++ b/llvm/test/Transforms/GCOVProfiling/kcfi-normalize.ll
@@ -1,8 +1,11 @@
-;; Ensure __llvm_gcov_(writeout|reset|init) have the correct !kcfi_type
+;; Ensure __llvm_gcov_(writeout|reset) have the correct !kcfi_type
 ;; with integer normalization.
 ; RUN: mkdir -p %t && cd %t
 ; RUN: opt < %s -S -passes=insert-gcov-profiling | FileCheck %s
 
+; Check for gcov initialization function pointers.
+; CHECK: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
+
 target triple = "x86_64-unknown-linux-gnu"
 
 define dso_local void @empty() !dbg !5 {
@@ -29,7 +32,5 @@ entry:
 ; CHECK-SAME: !kcfi_type ![[#TYPE:]]
 ; CHECK: define internal void @__llvm_gcov_reset()
 ; CHECK-SAME: !kcfi_type ![[#TYPE]]
-; CHECK: define internal void @__llvm_gcov_init()
-; CHECK-SAME: !kcfi_type ![[#TYPE]]
 
 ; CHECK: ![[#TYPE]] = !{i32 -440107680}
diff --git a/llvm/test/Transforms/GCOVProfiling/kcfi.ll b/llvm/test/Transforms/GCOVProfiling/kcfi.ll
index 1b97d25294cd65..35f863e0fd0752 100644
--- a/llvm/test/Transforms/GCOVProfiling/kcfi.ll
+++ b/llvm/test/Transforms/GCOVProfiling/kcfi.ll
@@ -1,7 +1,10 @@
-;; Ensure __llvm_gcov_(writeout|reset|init) have !kcfi_type with KCFI.
+;; Ensure __llvm_gcov_(writeout|reset) have !kcfi_type with KCFI.
 ; RUN: mkdir -p %t && cd %t
 ; RUN: opt < %s -S -passes=insert-gcov-profiling | FileCheck %s
 
+; Check for gcov initialization function pointers.
+; CHECK: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
+
 target triple = "x86_64-unknown-linux-gnu"
 
 define dso_local void @empty() !dbg !5 {
@@ -27,7 +30,5 @@ entry:
 ; CHECK-SAME: !kcfi_type ![[#TYPE:]]
 ; CHECK: define internal void @__llvm_gcov_reset()
 ; CHECK-SAME: !kcfi_type ![[#TYPE]]
-; CHECK: define internal void @__llvm_gcov_init()
-; CHECK-SAME: !kcfi_type ![[#TYPE]]
 
 ; CHECK: ![[#TYPE]] = !{i32 -1522505972}
diff --git a/llvm/test/Transforms/GCOVProfiling/module-flags.ll b/llvm/test/Transforms/GCOVProfiling/module-flags.ll
index 919dd41ea20348..ac8f983055985c 100644
--- a/llvm/test/Transforms/GCOVProfiling/module-flags.ll
+++ b/llvm/test/Transforms/GCOVProfiling/module-flags.ll
@@ -1,6 +1,9 @@
 ; RUN: mkdir -p %t && cd %t
 ; RUN: opt < %s -S -passes=insert-gcov-profiling | FileCheck %s
 
+; Check for gcov initialization function pointers.
+; CHECK: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
+
 target triple = "x86_64-unknown-linux-gnu"
 
 define dso_local void @empty() !dbg !5 {
@@ -30,5 +33,4 @@ entry:
 ;; Infer uwtable and "frame-pointer" from the module flags.
 ; CHECK: define internal void @__llvm_gcov_writeout() unnamed_addr #[[#ATTR:]]
 ; CHECK: define internal void @__llvm_gcov_reset() unnamed_addr #[[#ATTR]]
-; CHECK: define internal void @__llvm_gcov_init() unnamed_addr #[[#ATTR]]
 ; CHECK: attributes #[[#ATTR]] = { noinline nounwind uwtable "frame-pointer"="all" }

>From 7c6b1d734a23ddbffc67fdafd171ea0f5515891d Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <qwu at ibm.com>
Date: Thu, 12 Sep 2024 17:43:42 -0400
Subject: [PATCH 2/7] Special code for AIX only.

---
 clang/test/CodeGen/attr-function-return.c     |  7 +---
 clang/test/CodeGen/code-coverage.c            | 26 ++++++++-----
 clang/test/CodeGen/coverage-target-attr.c     |  5 +--
 compiler-rt/lib/profile/GCDAProfiling.c       |  2 +
 .../Instrumentation/GCOVProfiling.cpp         | 37 ++++++++++++++++++-
 .../GCOVProfiling/kcfi-normalize.ll           | 17 ++++++---
 llvm/test/Transforms/GCOVProfiling/kcfi.ll    | 17 ++++++---
 .../Transforms/GCOVProfiling/module-flags.ll  | 14 ++++---
 8 files changed, 88 insertions(+), 37 deletions(-)

diff --git a/clang/test/CodeGen/attr-function-return.c b/clang/test/CodeGen/attr-function-return.c
index 9c5cbca5640843..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
@@ -17,9 +17,6 @@
 // RUN:  -mfunction-return=thunk-extern -fsanitize=thread \
 // RUN:   | FileCheck %s --check-prefix=CHECK-TSAN
 
-// Check for gcov initialization function pointers.
-// CHECK-GCOV: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
-
 #if !__has_attribute(function_return)
 #error "missing attribute support for function_return"
 #endif
@@ -107,7 +104,7 @@ void no_attrs(void) {}
 // Test synthetic functions.
 // CHECK-GCOV: @__llvm_gcov_writeout() unnamed_addr [[EXTERNGCOV:#[0-9]+]]
 // CHECK-GCOV: @__llvm_gcov_reset() unnamed_addr [[EXTERNGCOV]]
-// CHECK-GCOV-NOT: @__llvm_gcov_init() unnamed_addr [[EXTERNGCOV]]
+// CHECK-GCOV: @__llvm_gcov_init() unnamed_addr [[EXTERNGCOV]]
 // CHECK-ASAN: @asan.module_ctor() [[EXTERNASAN:#[0-9]+]]
 // CHECK-TSAN: @tsan.module_ctor() [[EXTERNTSAN:#[0-9]+]]
 
diff --git a/clang/test/CodeGen/code-coverage.c b/clang/test/CodeGen/code-coverage.c
index 4e3f0aba8c4098..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:
@@ -49,13 +58,10 @@ int test2(int b) {
 /// 0x3430382a '4' '0' '8' '*'
 // 408-SAME: i32 875575338
 
-// Check for gcov initialization function pointers.
-// CHECK: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
-
 // Check that the noredzone flag is set on the generated functions.
 
 // CHECK: void @__llvm_gcov_writeout() unnamed_addr [[NRZ:#[0-9]+]]
-// CHECK-NOT: void @__llvm_gcov_init() unnamed_addr [[NRZ]]
+// CHECK-ELF: void @__llvm_gcov_init() unnamed_addr [[NRZ]]
 
 // CHECK: attributes [[NRZ]] = { {{.*}}noredzone{{.*}} }
 
diff --git a/clang/test/CodeGen/coverage-target-attr.c b/clang/test/CodeGen/coverage-target-attr.c
index 156e48ba6a31a2..d46299f5bee223 100644
--- a/clang/test/CodeGen/coverage-target-attr.c
+++ b/clang/test/CodeGen/coverage-target-attr.c
@@ -1,12 +1,9 @@
 // RUN: %clang_cc1 -emit-llvm -coverage-notes-file=/dev/null -coverage-data-file=/dev/null -triple aarch64-linux-android30 -target-cpu generic -target-feature +tagged-globals -fsanitize=hwaddress %s -o %t
 // RUN: FileCheck %s < %t
 
-// Check for gcov initialization function pointers.
-// CHECK: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
-
 // CHECK: define internal void @__llvm_gcov_writeout() unnamed_addr [[ATTR:#[0-9]+]]
 // CHECK: define internal void @__llvm_gcov_reset() unnamed_addr [[ATTR]]
-// CHECK-NOT: define internal void @__llvm_gcov_init() unnamed_addr [[ATTR]]
+// CHECK: define internal void @__llvm_gcov_init() unnamed_addr [[ATTR]]
 // CHECK: define internal void @hwasan.module_ctor() [[ATTR2:#[0-9]+]]
 // CHECK: attributes [[ATTR]] = {{.*}} "target-cpu"="generic" "target-features"="+tagged-globals"
 // CHECK: attributes [[ATTR2]] = {{.*}} "target-cpu"="generic" "target-features"="+tagged-globals"
diff --git a/compiler-rt/lib/profile/GCDAProfiling.c b/compiler-rt/lib/profile/GCDAProfiling.c
index f430dd4e36ce37..452a62fcc9d21e 100644
--- a/compiler-rt/lib/profile/GCDAProfiling.c
+++ b/compiler-rt/lib/profile/GCDAProfiling.c
@@ -624,6 +624,7 @@ 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 =
@@ -640,6 +641,7 @@ __llvm_profile_gcov_initialize() {
     llvm_gcov_init(wfn, rfn);
   }
 }
+#endif
 
 void __gcov_dump(void) {
   for (struct fn_node *f = writeout_fn_list.head; f; f = f->next)
diff --git a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
index e6a7cdf3f80a9e..4a399ad438215a 100644
--- a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
+++ b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
@@ -122,6 +122,9 @@ 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);
 
@@ -982,7 +985,11 @@ bool GCOVProfiler::emitProfileNotes(
     }
 
     if (EmitGCDA) {
-      emitModuleInitFunctionPtrs(CountersBySP);
+      const llvm::Triple &Triple = llvm::Triple(M->getTargetTriple());
+      if (Triple.getObjectFormat() == llvm::Triple::XCOFF)
+        emitModuleInitFunctionPtrs(CountersBySP);
+      else
+        emitGlobalConstructor(CountersBySP);
       EmitGCDA = false;
     }
   }
@@ -1003,6 +1010,34 @@ Function *GCOVProfiler::createInternalFunction(FunctionType *FTy,
   return F;
 }
 
+void GCOVProfiler::emitGlobalConstructor(
+    SmallVectorImpl<std::pair<GlobalVariable *, MDNode *>> &CountersBySP) {
+  Function *WriteoutF = insertCounterWriteout(CountersBySP);
+  Function *ResetF = insertReset(CountersBySP);
+
+  // Create a small bit of code that registers the "__llvm_gcov_writeout" to
+  // be executed at exit and the "__llvm_gcov_reset" function to be executed
+  // when "__gcov_flush" is called.
+  FunctionType *FTy = FunctionType::get(Type::getVoidTy(*Ctx), false);
+  Function *F = createInternalFunction(FTy, "__llvm_gcov_init", "_ZTSFvvE");
+  F->addFnAttr(Attribute::NoInline);
+
+  BasicBlock *BB = BasicBlock::Create(*Ctx, "entry", F);
+  IRBuilder<> Builder(BB);
+
+  FTy = FunctionType::get(Type::getVoidTy(*Ctx), false);
+  auto *PFTy = PointerType::get(FTy, 0);
+  FTy = FunctionType::get(Builder.getVoidTy(), {PFTy, PFTy}, false);
+
+  // Initialize the environment and register the local writeout, flush and
+  // reset functions.
+  FunctionCallee GCOVInit = M->getOrInsertFunction("llvm_gcov_init", FTy);
+  Builder.CreateCall(GCOVInit, {WriteoutF, ResetF});
+  Builder.CreateRetVoid();
+
+  appendToGlobalCtors(*M, F, 0);
+}
+
 void GCOVProfiler::emitModuleInitFunctionPtrs(
     SmallVectorImpl<std::pair<GlobalVariable *, MDNode *>> &CountersBySP) {
   Function *WriteoutF = insertCounterWriteout(CountersBySP);
diff --git a/llvm/test/Transforms/GCOVProfiling/kcfi-normalize.ll b/llvm/test/Transforms/GCOVProfiling/kcfi-normalize.ll
index be53595e9201a0..fa19fcfe271e07 100644
--- a/llvm/test/Transforms/GCOVProfiling/kcfi-normalize.ll
+++ b/llvm/test/Transforms/GCOVProfiling/kcfi-normalize.ll
@@ -1,12 +1,15 @@
-;; Ensure __llvm_gcov_(writeout|reset) have the correct !kcfi_type
+;; Ensure __llvm_gcov_(writeout|reset|init) have the correct !kcfi_type
 ;; with integer normalization.
 ; RUN: mkdir -p %t && cd %t
-; RUN: opt < %s -S -passes=insert-gcov-profiling | FileCheck %s
+; RUN: opt < %s -S -passes=insert-gcov-profiling \
+; RUN:  -mtriple=x86_64-unknown-linux-gnu | FileCheck \
+; RUN:  --check-prefixes=CHECK,CHECK-ELF %s
+; RUN: opt < %s -S -passes=insert-gcov-profiling \
+; RUN:  -mtriple=powerpc64-ibm-aix | FileCheck \
+; RUN:  --check-prefixes=CHECK,CHECK-XCOFF %s
 
-; Check for gcov initialization function pointers.
-; CHECK: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
-
-target triple = "x86_64-unknown-linux-gnu"
+; Check for gcov initialization function pointers for XCOFF.
+; CHECK-XCOFF: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
 
 define dso_local void @empty() !dbg !5 {
 entry:
@@ -32,5 +35,7 @@ entry:
 ; CHECK-SAME: !kcfi_type ![[#TYPE:]]
 ; CHECK: define internal void @__llvm_gcov_reset()
 ; CHECK-SAME: !kcfi_type ![[#TYPE]]
+; CHECK-ELF: define internal void @__llvm_gcov_init()
+; CHECK-ELF-SAME: !kcfi_type ![[#TYPE]]
 
 ; CHECK: ![[#TYPE]] = !{i32 -440107680}
diff --git a/llvm/test/Transforms/GCOVProfiling/kcfi.ll b/llvm/test/Transforms/GCOVProfiling/kcfi.ll
index 35f863e0fd0752..bfa3a6403578f2 100644
--- a/llvm/test/Transforms/GCOVProfiling/kcfi.ll
+++ b/llvm/test/Transforms/GCOVProfiling/kcfi.ll
@@ -1,11 +1,14 @@
-;; Ensure __llvm_gcov_(writeout|reset) have !kcfi_type with KCFI.
+;; Ensure __llvm_gcov_(writeout|reset|init) have !kcfi_type with KCFI.
 ; RUN: mkdir -p %t && cd %t
-; RUN: opt < %s -S -passes=insert-gcov-profiling | FileCheck %s
+; RUN: opt < %s -S -passes=insert-gcov-profiling \
+; RUN:  -mtriple=x86_64-unknown-linux-gnu | FileCheck \
+; RUN:  --check-prefixes=CHECK,CHECK-ELF %s
+; RUN: opt < %s -S -passes=insert-gcov-profiling \
+; RUN:  -mtriple=powerpc64-ibm-aix | FileCheck \
+; RUN:  --check-prefixes=CHECK,CHECK-XCOFF %s
 
-; Check for gcov initialization function pointers.
-; CHECK: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
-
-target triple = "x86_64-unknown-linux-gnu"
+; Check for gcov initialization function pointers for XCOFF.
+; CHECK-XCOFF: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
 
 define dso_local void @empty() !dbg !5 {
 entry:
@@ -30,5 +33,7 @@ entry:
 ; CHECK-SAME: !kcfi_type ![[#TYPE:]]
 ; CHECK: define internal void @__llvm_gcov_reset()
 ; CHECK-SAME: !kcfi_type ![[#TYPE]]
+; CHECK-ELF: define internal void @__llvm_gcov_init()
+; CHECK-ELF-SAME: !kcfi_type ![[#TYPE]]
 
 ; CHECK: ![[#TYPE]] = !{i32 -1522505972}
diff --git a/llvm/test/Transforms/GCOVProfiling/module-flags.ll b/llvm/test/Transforms/GCOVProfiling/module-flags.ll
index ac8f983055985c..77fc77bfe18cc3 100644
--- a/llvm/test/Transforms/GCOVProfiling/module-flags.ll
+++ b/llvm/test/Transforms/GCOVProfiling/module-flags.ll
@@ -1,10 +1,13 @@
 ; RUN: mkdir -p %t && cd %t
-; RUN: opt < %s -S -passes=insert-gcov-profiling | FileCheck %s
+; RUN: opt < %s -S -passes=insert-gcov-profiling \
+; RUN:  -mtriple=x86_64-unknown-linux-gnu | FileCheck \
+; RUN:  --check-prefixes=CHECK,CHECK-ELF %s
+; RUN: opt < %s -S -passes=insert-gcov-profiling \
+; RUN:  -mtriple=powerpc64-ibm-aix | FileCheck \
+; RUN:  --check-prefixes=CHECK,CHECK-XCOFF %s
 
-; Check for gcov initialization function pointers.
-; CHECK: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
-
-target triple = "x86_64-unknown-linux-gnu"
+; Check for gcov initialization function pointers for XCOFF.
+; CHECK-XCOFF: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
 
 define dso_local void @empty() !dbg !5 {
 entry:
@@ -33,4 +36,5 @@ entry:
 ;; Infer uwtable and "frame-pointer" from the module flags.
 ; CHECK: define internal void @__llvm_gcov_writeout() unnamed_addr #[[#ATTR:]]
 ; CHECK: define internal void @__llvm_gcov_reset() unnamed_addr #[[#ATTR]]
+; CHECK-ELF: define internal void @__llvm_gcov_init() unnamed_addr #[[#ATTR]]
 ; CHECK: attributes #[[#ATTR]] = { noinline nounwind uwtable "frame-pointer"="all" }

>From 258103e9a6813e353f203d4ca10db059a0d0902d Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <qwu at ibm.com>
Date: Mon, 16 Sep 2024 09:27:07 -0400
Subject: [PATCH 3/7] Fix lit test.

---
 clang/test/CodeGen/code-coverage.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/test/CodeGen/code-coverage.c b/clang/test/CodeGen/code-coverage.c
index 30c59d58380def..b65a1fe118b738 100644
--- a/clang/test/CodeGen/code-coverage.c
+++ b/clang/test/CodeGen/code-coverage.c
@@ -29,9 +29,6 @@
 // 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:
@@ -58,6 +55,9 @@ int test2(int b) {
 /// 0x3430382a '4' '0' '8' '*'
 // 408-SAME: i32 875575338
 
+// 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"
+
 // Check that the noredzone flag is set on the generated functions.
 
 // CHECK: void @__llvm_gcov_writeout() unnamed_addr [[NRZ:#[0-9]+]]

>From b1eaf18d91252feb3826afc53bc8975c86848bde Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <qwu at ibm.com>
Date: Wed, 18 Sep 2024 10:37:46 -0400
Subject: [PATCH 4/7] Improve new test.

---
 compiler-rt/test/profile/AIX/gcov-undef-sym.test | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/compiler-rt/test/profile/AIX/gcov-undef-sym.test b/compiler-rt/test/profile/AIX/gcov-undef-sym.test
index e377b321ca66cd..db9053952d95b7 100644
--- a/compiler-rt/test/profile/AIX/gcov-undef-sym.test
+++ b/compiler-rt/test/profile/AIX/gcov-undef-sym.test
@@ -6,7 +6,7 @@ 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: %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
 
@@ -15,7 +15,7 @@ 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: %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
 
@@ -24,7 +24,7 @@ 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: %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
 

>From 11793f38a9a7b6d05cd2cfdf5c1a09f3bf3cc5c3 Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <qwu at ibm.com>
Date: Fri, 4 Oct 2024 17:55:23 -0400
Subject: [PATCH 5/7] Address review comments.

---
 clang/test/CodeGen/attr-function-return.c     |   2 +-
 clang/test/CodeGen/code-coverage.c            |  16 +--
 compiler-rt/include/profile/InstrProfData.inc |   3 +-
 .../llvm/ProfileData/InstrProfData.inc        |   3 +-
 .../Instrumentation/GCOVProfiling.cpp         |   4 +-
 .../test/CodeGen/PowerPC/gcov_ctr_ref_init.ll | 126 ++++++++++++++++++
 .../GCOVProfiling/kcfi-normalize.ll           |  13 +-
 llvm/test/Transforms/GCOVProfiling/kcfi.ll    |  13 +-
 .../Transforms/GCOVProfiling/module-flags.ll  |  11 +-
 9 files changed, 162 insertions(+), 29 deletions(-)
 create mode 100644 llvm/test/CodeGen/PowerPC/gcov_ctr_ref_init.ll

diff --git a/clang/test/CodeGen/attr-function-return.c b/clang/test/CodeGen/attr-function-return.c
index 1aca3b1bfa5b59..df2cabf28693a3 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-prefixes=CHECK-GCOV
+// RUN:   | FileCheck %s --check-prefix=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 b65a1fe118b738..4e3364df217854 100644
--- a/clang/test/CodeGen/code-coverage.c
+++ b/clang/test/CodeGen/code-coverage.c
@@ -4,17 +4,17 @@
 /// 4.8 (default, compatible with gcov 7) emits the exit block the second.
 // RUN: rm -rf %t && mkdir %t && cd %t
 // 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:   FileCheck --check-prefixes=CHECK,CHECK-CTOR-INIT,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:   FileCheck --check-prefixes=CHECK,CHECK-CTOR-INIT,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:   FileCheck --check-prefixes=CHECK,CHECK-CTOR-INIT,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:   FileCheck --check-prefixes=CHECK,CHECK-RT-INIT,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:   FileCheck --check-prefixes=CHECK,CHECK-RT-INIT,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:   FileCheck --check-prefixes=CHECK,CHECK-RT-INIT,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
 
@@ -56,12 +56,12 @@ int test2(int b) {
 // 408-SAME: i32 875575338
 
 // 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"
+// CHECK-RT-INIT: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
 
 // Check that the noredzone flag is set on the generated functions.
 
 // CHECK: void @__llvm_gcov_writeout() unnamed_addr [[NRZ:#[0-9]+]]
-// CHECK-ELF: void @__llvm_gcov_init() unnamed_addr [[NRZ]]
+// CHECK-CTOR-INIT: 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 591fc1401e1aab..c66b0465a0b548 100644
--- a/compiler-rt/include/profile/InstrProfData.inc
+++ b/compiler-rt/include/profile/InstrProfData.inc
@@ -798,7 +798,8 @@ 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.
+// FIXME: Placeholder for Windows. Windows currently does not initialize
+// the GCOV functions in the runtime.
 #define INSTR_PROF_COVINIT_COFF ".lcovd$M"
 
 #ifdef _WIN32
diff --git a/llvm/include/llvm/ProfileData/InstrProfData.inc b/llvm/include/llvm/ProfileData/InstrProfData.inc
index 591fc1401e1aab..c66b0465a0b548 100644
--- a/llvm/include/llvm/ProfileData/InstrProfData.inc
+++ b/llvm/include/llvm/ProfileData/InstrProfData.inc
@@ -798,7 +798,8 @@ 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.
+// FIXME: Placeholder for Windows. Windows currently does not initialize
+// the GCOV functions in the runtime.
 #define INSTR_PROF_COVINIT_COFF ".lcovd$M"
 
 #ifdef _WIN32
diff --git a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
index 4a399ad438215a..3cc9484dfe73cd 100644
--- a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
+++ b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
@@ -918,7 +918,9 @@ bool GCOVProfiler::emitProfileNotes(
         GlobalVariable *Counters = new GlobalVariable(
             *M, CounterTy, false, GlobalValue::InternalLinkage,
             Constant::getNullValue(CounterTy), "__llvm_gcov_ctr");
-        Counters->setSection("__llvm_gcov_ctr_section");
+        const llvm::Triple &Triple = llvm::Triple(M->getTargetTriple());
+        if (Triple.getObjectFormat() == llvm::Triple::XCOFF)
+          Counters->setSection("__llvm_gcov_ctr_section");
         CountersBySP.emplace_back(Counters, SP);
 
         for (size_t I : llvm::seq<size_t>(0, Measured)) {
diff --git a/llvm/test/CodeGen/PowerPC/gcov_ctr_ref_init.ll b/llvm/test/CodeGen/PowerPC/gcov_ctr_ref_init.ll
new file mode 100644
index 00000000000000..91f55fd71aa19a
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/gcov_ctr_ref_init.ll
@@ -0,0 +1,126 @@
+; Tests if the __llvm_gcov_ctr section contains a .ref pseudo-op
+; referring to the __llvm_covinit section.
+; RUN: llc < %s | FileCheck %s
+
+target datalayout = "E-m:a-p:32:32-Fi32-i64:64-n32"
+target triple = "powerpc-ibm-aix"
+
+; CHECK: .csect __llvm_covinit[RW],3
+; CHECK:         .vbyte  4, __llvm_gcov_writeout[DS]
+; CHECK-NEXT:    .vbyte  4, __llvm_gcov_reset[DS]
+; CHECK: __llvm_gcov_ctr.1:
+; CHECK-NEXT:     .extern .llvm_gcda_start_file[PR]
+; CHECK-NEXT:    .extern .llvm_gcda_emit_function[PR]
+; CHECK-NEXT:    .extern .llvm_gcda_emit_arcs[PR]
+; CHECK-NEXT:    .extern .llvm_gcda_summary_info[PR]
+; CHECK-NEXT:    .extern .llvm_gcda_end_file[PR]
+; CHECK-NEXT:    .ref __llvm_covinit[RW]
+
+%emit_function_args_ty = type { i32, i32, i32 }
+%emit_arcs_args_ty = type { i32, ptr }
+%file_info = type { %start_file_args_ty, i32, ptr, ptr }
+%start_file_args_ty = type { ptr, i32, i32 }
+
+ at __llvm_gcov_ctr = internal global [1 x i64] zeroinitializer, section "__llvm_gcov_ctr_section"
+ at __llvm_gcov_ctr.1 = internal global [1 x i64] zeroinitializer, section "__llvm_gcov_ctr_section"
+ at 0 = private unnamed_addr constant [10 x i8] c"test.gcda\00", align 1
+ at __llvm_internal_gcov_emit_function_args.0 = internal unnamed_addr constant [2 x %emit_function_args_ty] [%emit_function_args_ty { i32 0, i32 1961870044, i32 -801444649 }, %emit_function_args_ty { i32 1, i32 1795396728, i32 -801444649 }]
+ at __llvm_internal_gcov_emit_arcs_args.0 = internal unnamed_addr constant [2 x %emit_arcs_args_ty] [%emit_arcs_args_ty { i32 1, ptr @__llvm_gcov_ctr }, %emit_arcs_args_ty { i32 1, ptr @__llvm_gcov_ctr.1 }]
+ at __llvm_internal_gcov_emit_file_info = internal unnamed_addr constant [1 x %file_info] [%file_info { %start_file_args_ty { ptr @0, i32 875575338, i32 -801444649 }, i32 2, ptr @__llvm_internal_gcov_emit_function_args.0, ptr @__llvm_internal_gcov_emit_arcs_args.0 }]
+ at __llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit", align 8
+
+define i32 @bar() {
+entry:
+  %gcov_ctr = load i64, ptr @__llvm_gcov_ctr, align 8
+  %0 = add i64 %gcov_ctr, 1
+  store i64 %0, ptr @__llvm_gcov_ctr, align 8
+  ret i32 1
+}
+
+define i32 @main() {
+entry:
+  %gcov_ctr = load i64, ptr @__llvm_gcov_ctr.1, align 8
+  %0 = add i64 %gcov_ctr, 1
+  store i64 %0, ptr @__llvm_gcov_ctr.1, align 8
+  %retval = alloca i32, align 4
+  store i32 0, ptr %retval, align 4
+  %call = call i32 @bar()
+  %sub = sub nsw i32 %call, 1
+  ret i32 %sub
+}
+
+define internal void @__llvm_gcov_writeout() unnamed_addr {
+entry:
+  br label %file.loop.header
+
+file.loop.header:                                 ; preds = %file.loop.latch, %entry
+  %file_idx = phi i32 [ 0, %entry ], [ %next_file_idx, %file.loop.latch ]
+  %0 = getelementptr inbounds [1 x %file_info], ptr @__llvm_internal_gcov_emit_file_info, i32 0, i32 %file_idx
+  %start_file_args = getelementptr inbounds nuw %file_info, ptr %0, i32 0, i32 0
+  %1 = getelementptr inbounds nuw %start_file_args_ty, ptr %start_file_args, i32 0, i32 0
+  %filename = load ptr, ptr %1, align 4
+  %2 = getelementptr inbounds nuw %start_file_args_ty, ptr %start_file_args, i32 0, i32 1
+  %version = load i32, ptr %2, align 4
+  %3 = getelementptr inbounds nuw %start_file_args_ty, ptr %start_file_args, i32 0, i32 2
+  %stamp = load i32, ptr %3, align 4
+  call void @llvm_gcda_start_file(ptr %filename, i32 %version, i32 %stamp)
+  %4 = getelementptr inbounds nuw %file_info, ptr %0, i32 0, i32 1
+  %num_ctrs = load i32, ptr %4, align 4
+  %5 = getelementptr inbounds nuw %file_info, ptr %0, i32 0, i32 2
+  %emit_function_args = load ptr, ptr %5, align 4
+  %6 = getelementptr inbounds nuw %file_info, ptr %0, i32 0, i32 3
+  %emit_arcs_args = load ptr, ptr %6, align 4
+  %7 = icmp slt i32 0, %num_ctrs
+  br i1 %7, label %counter.loop.header, label %file.loop.latch
+
+counter.loop.header:                              ; preds = %counter.loop.header, %file.loop.header
+  %ctr_idx = phi i32 [ 0, %file.loop.header ], [ %15, %counter.loop.header ]
+  %8 = getelementptr inbounds %emit_function_args_ty, ptr %emit_function_args, i32 %ctr_idx
+  %9 = getelementptr inbounds nuw %emit_function_args_ty, ptr %8, i32 0, i32 0
+  %ident = load i32, ptr %9, align 4
+  %10 = getelementptr inbounds nuw %emit_function_args_ty, ptr %8, i32 0, i32 1
+  %func_checkssum = load i32, ptr %10, align 4
+  %11 = getelementptr inbounds nuw %emit_function_args_ty, ptr %8, i32 0, i32 2
+  %cfg_checksum = load i32, ptr %11, align 4
+  call void @llvm_gcda_emit_function(i32 %ident, i32 %func_checkssum, i32 %cfg_checksum)
+  %12 = getelementptr inbounds %emit_arcs_args_ty, ptr %emit_arcs_args, i32 %ctr_idx
+  %13 = getelementptr inbounds nuw %emit_arcs_args_ty, ptr %12, i32 0, i32 0
+  %num_counters = load i32, ptr %13, align 4
+  %14 = getelementptr inbounds nuw %emit_arcs_args_ty, ptr %12, i32 0, i32 1
+  %counters = load ptr, ptr %14, align 4
+  call void @llvm_gcda_emit_arcs(i32 %num_counters, ptr %counters)
+  %15 = add i32 %ctr_idx, 1
+  %16 = icmp slt i32 %15, %num_ctrs
+  br i1 %16, label %counter.loop.header, label %file.loop.latch
+
+file.loop.latch:                                  ; preds = %counter.loop.header, %file.loop.header
+  call void @llvm_gcda_summary_info()
+  call void @llvm_gcda_end_file()
+  %next_file_idx = add i32 %file_idx, 1
+  %17 = icmp slt i32 %next_file_idx, 1
+  br i1 %17, label %file.loop.header, label %exit
+
+exit:                                             ; preds = %file.loop.latch
+  ret void
+}
+
+declare void @llvm_gcda_start_file(ptr, i32, i32)
+
+declare void @llvm_gcda_emit_function(i32, i32, i32)
+
+declare void @llvm_gcda_emit_arcs(i32, ptr)
+
+declare void @llvm_gcda_summary_info()
+
+declare void @llvm_gcda_end_file()
+
+define internal void @__llvm_gcov_reset() unnamed_addr {
+entry:
+  call void @llvm.memset.p0.i64(ptr @__llvm_gcov_ctr, i8 0, i64 8, i1 false)
+  call void @llvm.memset.p0.i64(ptr @__llvm_gcov_ctr.1, i8 0, i64 8, i1 false)
+  ret void
+}
+
+declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg)
+
+
diff --git a/llvm/test/Transforms/GCOVProfiling/kcfi-normalize.ll b/llvm/test/Transforms/GCOVProfiling/kcfi-normalize.ll
index fa19fcfe271e07..9ad0418025e56c 100644
--- a/llvm/test/Transforms/GCOVProfiling/kcfi-normalize.ll
+++ b/llvm/test/Transforms/GCOVProfiling/kcfi-normalize.ll
@@ -3,13 +3,14 @@
 ; RUN: mkdir -p %t && cd %t
 ; RUN: opt < %s -S -passes=insert-gcov-profiling \
 ; RUN:  -mtriple=x86_64-unknown-linux-gnu | FileCheck \
-; RUN:  --check-prefixes=CHECK,CHECK-ELF %s
+; RUN:  --check-prefixes=CHECK,CHECK-CTOR-INIT %s
 ; RUN: opt < %s -S -passes=insert-gcov-profiling \
 ; RUN:  -mtriple=powerpc64-ibm-aix | FileCheck \
-; RUN:  --check-prefixes=CHECK,CHECK-XCOFF %s
+; RUN:  --check-prefixes=CHECK,CHECK-RT-INIT %s
 
-; Check for gcov initialization function pointers for XCOFF.
-; CHECK-XCOFF: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
+; Check for gcov initialization function pointers when we initialize
+; the writeout and reset functions in the runtime.
+; CHECK-RT-INIT: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
 
 define dso_local void @empty() !dbg !5 {
 entry:
@@ -35,7 +36,7 @@ entry:
 ; CHECK-SAME: !kcfi_type ![[#TYPE:]]
 ; CHECK: define internal void @__llvm_gcov_reset()
 ; CHECK-SAME: !kcfi_type ![[#TYPE]]
-; CHECK-ELF: define internal void @__llvm_gcov_init()
-; CHECK-ELF-SAME: !kcfi_type ![[#TYPE]]
+; CHECK-CTOR-INIT: define internal void @__llvm_gcov_init()
+; CHECK-CTOR-INIT-SAME: !kcfi_type ![[#TYPE]]
 
 ; CHECK: ![[#TYPE]] = !{i32 -440107680}
diff --git a/llvm/test/Transforms/GCOVProfiling/kcfi.ll b/llvm/test/Transforms/GCOVProfiling/kcfi.ll
index bfa3a6403578f2..5e0e91fc92f5f7 100644
--- a/llvm/test/Transforms/GCOVProfiling/kcfi.ll
+++ b/llvm/test/Transforms/GCOVProfiling/kcfi.ll
@@ -2,13 +2,14 @@
 ; RUN: mkdir -p %t && cd %t
 ; RUN: opt < %s -S -passes=insert-gcov-profiling \
 ; RUN:  -mtriple=x86_64-unknown-linux-gnu | FileCheck \
-; RUN:  --check-prefixes=CHECK,CHECK-ELF %s
+; RUN:  --check-prefixes=CHECK,CHECK-CTOR-INIT %s
 ; RUN: opt < %s -S -passes=insert-gcov-profiling \
 ; RUN:  -mtriple=powerpc64-ibm-aix | FileCheck \
-; RUN:  --check-prefixes=CHECK,CHECK-XCOFF %s
+; RUN:  --check-prefixes=CHECK,CHECK-RT-INIT %s
 
-; Check for gcov initialization function pointers for XCOFF.
-; CHECK-XCOFF: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
+; Check for gcov initialization function pointers when we initialize
+; the writeout and reset functions in the runtime.
+; CHECK-RT-INIT: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
 
 define dso_local void @empty() !dbg !5 {
 entry:
@@ -33,7 +34,7 @@ entry:
 ; CHECK-SAME: !kcfi_type ![[#TYPE:]]
 ; CHECK: define internal void @__llvm_gcov_reset()
 ; CHECK-SAME: !kcfi_type ![[#TYPE]]
-; CHECK-ELF: define internal void @__llvm_gcov_init()
-; CHECK-ELF-SAME: !kcfi_type ![[#TYPE]]
+; CHECK-CTOR-INIT: define internal void @__llvm_gcov_init()
+; CHECK-CTOR-INIT-SAME: !kcfi_type ![[#TYPE]]
 
 ; CHECK: ![[#TYPE]] = !{i32 -1522505972}
diff --git a/llvm/test/Transforms/GCOVProfiling/module-flags.ll b/llvm/test/Transforms/GCOVProfiling/module-flags.ll
index 77fc77bfe18cc3..59f116d0d7e655 100644
--- a/llvm/test/Transforms/GCOVProfiling/module-flags.ll
+++ b/llvm/test/Transforms/GCOVProfiling/module-flags.ll
@@ -1,13 +1,14 @@
 ; RUN: mkdir -p %t && cd %t
 ; RUN: opt < %s -S -passes=insert-gcov-profiling \
 ; RUN:  -mtriple=x86_64-unknown-linux-gnu | FileCheck \
-; RUN:  --check-prefixes=CHECK,CHECK-ELF %s
+; RUN:  --check-prefixes=CHECK,CHECK-CTOR-INIT %s
 ; RUN: opt < %s -S -passes=insert-gcov-profiling \
 ; RUN:  -mtriple=powerpc64-ibm-aix | FileCheck \
-; RUN:  --check-prefixes=CHECK,CHECK-XCOFF %s
+; RUN:  --check-prefixes=CHECK,CHECK-RT-INIT %s
 
-; Check for gcov initialization function pointers for XCOFF.
-; CHECK-XCOFF: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
+; Check for gcov initialization function pointers when we initialize
+; the writeout and reset functions in the runtime.
+; CHECK-RT-INIT: @__llvm_covinit_functions = private constant { ptr, ptr } { ptr @__llvm_gcov_writeout, ptr @__llvm_gcov_reset }, section "__llvm_covinit"
 
 define dso_local void @empty() !dbg !5 {
 entry:
@@ -36,5 +37,5 @@ entry:
 ;; Infer uwtable and "frame-pointer" from the module flags.
 ; CHECK: define internal void @__llvm_gcov_writeout() unnamed_addr #[[#ATTR:]]
 ; CHECK: define internal void @__llvm_gcov_reset() unnamed_addr #[[#ATTR]]
-; CHECK-ELF: define internal void @__llvm_gcov_init() unnamed_addr #[[#ATTR]]
+; CHECK-CTOR-INIT: define internal void @__llvm_gcov_init() unnamed_addr #[[#ATTR]]
 ; CHECK: attributes #[[#ATTR]] = { noinline nounwind uwtable "frame-pointer"="all" }

>From b7c470ad6ce64bbd4cd7d513be98a57f7d1740f1 Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <qwu at ibm.com>
Date: Fri, 4 Oct 2024 19:20:02 -0400
Subject: [PATCH 6/7] Fix the code that inserts the .ref pseudo-op when
 xcoff-roptr is in effect.

---
 llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp      | 9 +++++++--
 llvm/test/CodeGen/PowerPC/gcov_ctr_ref_init.ll | 9 ++++++---
 2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
index 3251340aa67a6a..e32ba66dd13d05 100644
--- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
+++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
@@ -2976,10 +2976,15 @@ void PPCAIXAsmPrinter::emitGCOVRefs() {
       /*MultiSymbolsAllowed*/ true);
 
   OutStreamer->switchSection(CtrSection);
+  const XCOFF::StorageMappingClass MappingClass =
+      TM.Options.XCOFFReadOnlyPointers ? XCOFF::XMC_RO : XCOFF::XMC_RW;
   if (OutContext.hasXCOFFSection(
           "__llvm_covinit",
-          XCOFF::CsectProperties(XCOFF::XMC_RW, XCOFF::XTY_SD))) {
-    MCSymbol *S = OutContext.getOrCreateSymbol("__llvm_covinit[RW]");
+          XCOFF::CsectProperties(MappingClass, XCOFF::XTY_SD))) {
+    const char *SymbolStr = TM.Options.XCOFFReadOnlyPointers
+                                ? "__llvm_covinit[RO]"
+                                : "__llvm_covinit[RW]";
+    MCSymbol *S = OutContext.getOrCreateSymbol(SymbolStr);
     OutStreamer->emitXCOFFRefDirective(S);
   }
 }
diff --git a/llvm/test/CodeGen/PowerPC/gcov_ctr_ref_init.ll b/llvm/test/CodeGen/PowerPC/gcov_ctr_ref_init.ll
index 91f55fd71aa19a..990fed30a059e4 100644
--- a/llvm/test/CodeGen/PowerPC/gcov_ctr_ref_init.ll
+++ b/llvm/test/CodeGen/PowerPC/gcov_ctr_ref_init.ll
@@ -1,11 +1,13 @@
 ; Tests if the __llvm_gcov_ctr section contains a .ref pseudo-op
 ; referring to the __llvm_covinit section.
-; RUN: llc < %s | FileCheck %s
+; RUN: llc < %s | FileCheck --check-prefixes=CHECK,CHECK-RW %s
+; RUN: llc -mxcoff-roptr < %s | FileCheck --check-prefixes=CHECK,CHECK-RO %s
 
 target datalayout = "E-m:a-p:32:32-Fi32-i64:64-n32"
 target triple = "powerpc-ibm-aix"
 
-; CHECK: .csect __llvm_covinit[RW],3
+; CHECK-RW: .csect __llvm_covinit[RW],3
+; CHECK-RO: .csect __llvm_covinit[RO],3
 ; CHECK:         .vbyte  4, __llvm_gcov_writeout[DS]
 ; CHECK-NEXT:    .vbyte  4, __llvm_gcov_reset[DS]
 ; CHECK: __llvm_gcov_ctr.1:
@@ -14,7 +16,8 @@ target triple = "powerpc-ibm-aix"
 ; CHECK-NEXT:    .extern .llvm_gcda_emit_arcs[PR]
 ; CHECK-NEXT:    .extern .llvm_gcda_summary_info[PR]
 ; CHECK-NEXT:    .extern .llvm_gcda_end_file[PR]
-; CHECK-NEXT:    .ref __llvm_covinit[RW]
+; CHECK-RW-NEXT:    .ref __llvm_covinit[RW]
+; CHECK-RO-NEXT:    .ref __llvm_covinit[RO]
 
 %emit_function_args_ty = type { i32, i32, i32 }
 %emit_arcs_args_ty = type { i32, ptr }

>From 9c8505ce3c05e1dda8e7677f6ca36db96f932261 Mon Sep 17 00:00:00 2001
From: Qiongsi Wu <qwu at ibm.com>
Date: Tue, 15 Oct 2024 12:24:29 -0400
Subject: [PATCH 7/7] Address code review.

---
 .../test/CodeGen/PowerPC/gcov_ctr_ref_init.ll | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/llvm/test/CodeGen/PowerPC/gcov_ctr_ref_init.ll b/llvm/test/CodeGen/PowerPC/gcov_ctr_ref_init.ll
index 990fed30a059e4..197d2c98e81134 100644
--- a/llvm/test/CodeGen/PowerPC/gcov_ctr_ref_init.ll
+++ b/llvm/test/CodeGen/PowerPC/gcov_ctr_ref_init.ll
@@ -8,14 +8,19 @@ target triple = "powerpc-ibm-aix"
 
 ; CHECK-RW: .csect __llvm_covinit[RW],3
 ; CHECK-RO: .csect __llvm_covinit[RO],3
-; CHECK:         .vbyte  4, __llvm_gcov_writeout[DS]
-; CHECK-NEXT:    .vbyte  4, __llvm_gcov_reset[DS]
-; CHECK: __llvm_gcov_ctr.1:
+; CHECK-NEXT:    .align  3                               # @__llvm_covinit_functions
+; CHECK-NEXT: L..__llvm_covinit_functions:
+; CHECK-NEXT:     .vbyte  4, __llvm_gcov_writeout[DS]
+; CHECK-NEXT:     .vbyte  4, __llvm_gcov_reset[DS]
+; CHECK: L.._MergedGlobals:
+; CHECK-NEXT: __llvm_gcov_ctr:
+; CHECK-NEXT:     .space  16
+; CHECK-NEXT: __llvm_gcov_ctr.1:
 ; CHECK-NEXT:     .extern .llvm_gcda_start_file[PR]
-; CHECK-NEXT:    .extern .llvm_gcda_emit_function[PR]
-; CHECK-NEXT:    .extern .llvm_gcda_emit_arcs[PR]
-; CHECK-NEXT:    .extern .llvm_gcda_summary_info[PR]
-; CHECK-NEXT:    .extern .llvm_gcda_end_file[PR]
+; CHECK-NEXT:     .extern .llvm_gcda_emit_function[PR]
+; CHECK-NEXT:     .extern .llvm_gcda_emit_arcs[PR]
+; CHECK-NEXT:     .extern .llvm_gcda_summary_info[PR]
+; CHECK-NEXT:     .extern .llvm_gcda_end_file[PR]
 ; CHECK-RW-NEXT:    .ref __llvm_covinit[RW]
 ; CHECK-RO-NEXT:    .ref __llvm_covinit[RO]
 



More information about the cfe-commits mailing list