[clang] f9d0789 - [PGO] Initialize GCOV Writeout and Reset Functions in the Runtime on AIX (#108570)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 17 06:32:17 PDT 2024
Author: Qiongsi Wu
Date: 2024-10-17T09:32:10-04:00
New Revision: f9d07890640434a4be0e7f651dd295478598b36d
URL: https://github.com/llvm/llvm-project/commit/f9d07890640434a4be0e7f651dd295478598b36d
DIFF: https://github.com/llvm/llvm-project/commit/f9d07890640434a4be0e7f651dd295478598b36d.diff
LOG: [PGO] Initialize GCOV Writeout and Reset Functions in the Runtime on AIX (#108570)
This PR registers the writeout and reset functions for `gcov` for all
modules in the PGO runtime, instead of registering them
using global constructors in each module. The change is made for AIX
only, but the same mechanism works on Linux on Power.
When registering such functions using global constructors in each module
without `-ffunction-sections`, the AIX linker cannot garbage collect
unused undefined symbols, because such symbols are grouped in the same
section as the `__sinit` symbol. Keeping such undefined symbols causes
link errors (see test case
https://github.com/llvm/llvm-project/pull/108570/files#diff-500a7e1ba871e1b6b61b523700d5e30987900002add306e1b5e4972cf6d5a4f1R1
for this scenario). This PR implements the initialization in the
runtime, hence avoiding introducing `__sinit` into each module.
The implementation adds a new global variable `__llvm_covinit_functions`
to each module. This new global variable contains the function pointers
to the `Writeout` and `Reset` functions. `__llvm_covinit_functions`'s
section is the named section `__llvm_covinit`. The linker will aggregate
all the `__llvm_covinit` sections from each module
to form one single named section in the final binary. The pair of
functions
```
const __llvm_gcov_init_func_struct *__llvm_profile_begin_covinit();
const __llvm_gcov_init_func_struct *__llvm_profile_end_covinit();
```
are implemented to return the start and end address of this named
section in the final binary, and they are used in function
```
__llvm_profile_gcov_initialize()
```
(which is a constructor function in the runtime) so the runtime knows
the addresses of all the `Writeout` and `Reset` functions from all the
modules.
One noticeable implementation detail relevant to AIX is that to preserve
the `__llvm_covinit` from the linker's garbage collection, a `.ref`
pseudo instruction is inserted into them, referring to the section that
contains the `__llvm_gcov_ctr` variables, which are used in the
instrumented code. The `__llvm_gcov_ctr` variables did not belong to
named sections before, but this PR added them to the
`__llvm_gcov_ctr_section` named section, so we can add a `.ref` pseudo
instruction that refers to them in the `__llvm_covinit` section.
Added:
compiler-rt/test/profile/AIX/gcov-undef-sym.test
llvm/test/CodeGen/PowerPC/gcov_ctr_ref_init.ll
Modified:
clang/test/CodeGen/code-coverage.c
compiler-rt/include/profile/InstrProfData.inc
compiler-rt/lib/profile/GCDAProfiling.c
compiler-rt/lib/profile/InstrProfiling.h
compiler-rt/lib/profile/InstrProfilingPlatformAIX.c
compiler-rt/lib/profile/InstrProfilingPlatformLinux.c
llvm/include/llvm/ProfileData/InstrProfData.inc
llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp
llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
llvm/test/Transforms/GCOVProfiling/kcfi-normalize.ll
llvm/test/Transforms/GCOVProfiling/kcfi.ll
llvm/test/Transforms/GCOVProfiling/module-flags.ll
Removed:
################################################################################
diff --git a/clang/test/CodeGen/code-coverage.c b/clang/test/CodeGen/code-coverage.c
index d7994bab35d81a..4e3364df217854 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-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-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-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-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-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-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
@@ -49,10 +55,13 @@ int test2(int b) {
/// 0x3430382a '4' '0' '8' '*'
// 408-SAME: i32 875575338
+// Check for gcov initialization function pointers.
+// 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: 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 b9df3266fbcf8f..c66b0465a0b548 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,10 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
#define INSTR_PROF_COVNAME_COFF ".lcovn"
#define INSTR_PROF_ORDERFILE_COFF ".lorderfile$M"
+// FIXME: Placeholder for Windows. Windows currently does not initialize
+// the GCOV functions in the runtime.
+#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 +821,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 +843,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 f67d95d21a7b54..ac01805e70adc4 100644
--- a/compiler-rt/lib/profile/GCDAProfiling.c
+++ b/compiler-rt/lib/profile/GCDAProfiling.c
@@ -624,6 +624,25 @@ void llvm_gcov_init(fn_ptr wfn, fn_ptr rfn) {
}
}
+#if defined(_AIX)
+COMPILER_RT_VISIBILITY __attribute__((constructor)) void
+__llvm_profile_gcov_initialize() {
+ const __llvm_gcov_init_func_struct *InitFuncStart =
+ __llvm_profile_begin_covinit();
+ const __llvm_gcov_init_func_struct *InitFuncEnd =
+ __llvm_profile_end_covinit();
+
+ for (const __llvm_gcov_init_func_struct *Ptr = InitFuncStart;
+ Ptr != InitFuncEnd; ++Ptr) {
+ fn_ptr wfn = (fn_ptr)Ptr->WriteoutFunction;
+ fn_ptr rfn = (fn_ptr)Ptr->ResetFunction;
+ if (!(wfn && rfn))
+ continue;
+ llvm_gcov_init(wfn, rfn);
+ }
+}
+#endif
+
void __gcov_dump(void) {
for (struct fn_node *f = writeout_fn_list.head; f; f = f->next)
f->fn();
diff --git a/compiler-rt/lib/profile/InstrProfiling.h b/compiler-rt/lib/profile/InstrProfiling.h
index 9e43fd7c4789d8..7f0c0c194dc919 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 02f23379ce98bf..e2c06d51e0c67c 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..db9053952d95b7
--- /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: %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: %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: %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..c66b0465a0b548 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,10 @@ serializeValueProfDataFrom(ValueProfRecordClosure *Closure,
#define INSTR_PROF_COVNAME_COFF ".lcovn"
#define INSTR_PROF_ORDERFILE_COFF ".lorderfile$M"
+// FIXME: Placeholder for Windows. Windows currently does not initialize
+// the GCOV functions in the runtime.
+#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 +821,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 +843,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 9528aea3497999..b5a6c1c6e01df8 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,31 @@ 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);
+ const XCOFF::StorageMappingClass MappingClass =
+ TM.Options.XCOFFReadOnlyPointers ? XCOFF::XMC_RO : XCOFF::XMC_RW;
+ if (OutContext.hasXCOFFSection(
+ "__llvm_covinit",
+ 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);
+ }
+}
+
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 +2996,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 a409f6150a71c1..2ea89be40a3d46 100644
--- a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
+++ b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
@@ -29,6 +29,7 @@
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
+#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/CRC.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
@@ -121,8 +122,11 @@ class GCOVProfiler {
Function *createInternalFunction(FunctionType *FTy, StringRef Name,
StringRef MangledType = "");
+
void emitGlobalConstructor(
SmallVectorImpl<std::pair<GlobalVariable *, MDNode *>> &CountersBySP);
+ void emitModuleInitFunctionPtrs(
+ SmallVectorImpl<std::pair<GlobalVariable *, MDNode *>> &CountersBySP);
bool isFunctionInstrumented(const Function &F);
std::vector<Regex> createRegexesFromString(StringRef RegexesStr);
@@ -913,6 +917,9 @@ bool GCOVProfiler::emitProfileNotes(
GlobalVariable *Counters = new GlobalVariable(
*M, CounterTy, false, GlobalValue::InternalLinkage,
Constant::getNullValue(CounterTy), "__llvm_gcov_ctr");
+ 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)) {
@@ -979,7 +986,11 @@ bool GCOVProfiler::emitProfileNotes(
}
if (EmitGCDA) {
- emitGlobalConstructor(CountersBySP);
+ const llvm::Triple &Triple = llvm::Triple(M->getTargetTriple());
+ if (Triple.getObjectFormat() == llvm::Triple::XCOFF)
+ emitModuleInitFunctionPtrs(CountersBySP);
+ else
+ emitGlobalConstructor(CountersBySP);
EmitGCDA = false;
}
}
@@ -1028,6 +1039,40 @@ void GCOVProfiler::emitGlobalConstructor(
appendToGlobalCtors(*M, F, 0);
}
+void GCOVProfiler::emitModuleInitFunctionPtrs(
+ SmallVectorImpl<std::pair<GlobalVariable *, MDNode *>> &CountersBySP) {
+ Function *WriteoutF = insertCounterWriteout(CountersBySP);
+ Function *ResetF = insertReset(CountersBySP);
+
+ // 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.
+
+ auto &Ctx = M->getContext();
+
+ Type *InitFuncDataTy[] = {
+#define COVINIT_FUNC(Type, LLVMType, Name, Init) LLVMType,
+#include "llvm/ProfileData/InstrProfData.inc"
+ };
+
+ auto STy = StructType::get(Ctx, ArrayRef(InitFuncDataTy));
+
+ Constant *InitFuncPtrs[] = {
+#define COVINIT_FUNC(Type, LLVMType, Name, Init) Init,
+#include "llvm/ProfileData/InstrProfData.inc"
+ };
+
+ 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) {
Type *Args[] = {
PointerType::getUnqual(*Ctx), // const char *orig_filename
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..4710d5c14e5b19
--- /dev/null
+++ b/llvm/test/CodeGen/PowerPC/gcov_ctr_ref_init.ll
@@ -0,0 +1,138 @@
+; Tests if the __llvm_gcov_ctr section contains a .ref pseudo-op
+; referring to the __llvm_covinit section.
+; 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-RW: .csect __llvm_covinit[RW],3
+; CHECK-RO: .csect __llvm_covinit[RO],3
+; 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: .csect __llvm_gcov_ctr_section[RW],3
+; CHECK-NEXT: .lglobl __llvm_gcov_ctr # @_MergedGlobals
+; CHECK-NEXT: .lglobl __llvm_gcov_ctr.1
+; CHECK-NEXT: .align 3
+; CHECK-NEXT: 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-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 }
+%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 19122b920d1ca4..9ad0418025e56c 100644
--- a/llvm/test/Transforms/GCOVProfiling/kcfi-normalize.ll
+++ b/llvm/test/Transforms/GCOVProfiling/kcfi-normalize.ll
@@ -1,9 +1,16 @@
;; 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-CTOR-INIT %s
+; RUN: opt < %s -S -passes=insert-gcov-profiling \
+; RUN: -mtriple=powerpc64-ibm-aix | FileCheck \
+; RUN: --check-prefixes=CHECK,CHECK-RT-INIT %s
-target triple = "x86_64-unknown-linux-gnu"
+; 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:
@@ -29,7 +36,7 @@ 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-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 1b97d25294cd65..5e0e91fc92f5f7 100644
--- a/llvm/test/Transforms/GCOVProfiling/kcfi.ll
+++ b/llvm/test/Transforms/GCOVProfiling/kcfi.ll
@@ -1,8 +1,15 @@
;; 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-CTOR-INIT %s
+; RUN: opt < %s -S -passes=insert-gcov-profiling \
+; RUN: -mtriple=powerpc64-ibm-aix | FileCheck \
+; RUN: --check-prefixes=CHECK,CHECK-RT-INIT %s
-target triple = "x86_64-unknown-linux-gnu"
+; 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:
@@ -27,7 +34,7 @@ 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-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 919dd41ea20348..59f116d0d7e655 100644
--- a/llvm/test/Transforms/GCOVProfiling/module-flags.ll
+++ b/llvm/test/Transforms/GCOVProfiling/module-flags.ll
@@ -1,7 +1,14 @@
; 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-CTOR-INIT %s
+; RUN: opt < %s -S -passes=insert-gcov-profiling \
+; RUN: -mtriple=powerpc64-ibm-aix | FileCheck \
+; RUN: --check-prefixes=CHECK,CHECK-RT-INIT %s
-target triple = "x86_64-unknown-linux-gnu"
+; 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:
@@ -30,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: 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" }
More information about the cfe-commits
mailing list