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

Qiongsi Wu via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 16 06:16:01 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/3] 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/3] 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/3] 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]+]]



More information about the cfe-commits mailing list