https://github.com/nicolas-guichard created 
https://github.com/llvm/llvm-project/pull/230077

GCC has a -fprofile-abs-path option to use absolute paths to source files in 
GCNO files. Citing GCC's documentation:
> Automatically convert relative source file names to absolute path names in 
> the .gcno files. This allows gcov to find the correct sources in projects 
> where compilations occur with different working directories.

This is particularly useful when doing unity builds where a generated source 
file includes many original source files, as otherwise the GCNO refers to the 
original source files by relative paths.

When the -fprofile-abs-path flag is passed to clang/clang++, the new 
-coverage-notes-abs-paths flag is passed to cc1, which calls 
vfs::FileSystem::makeAbsolute to get the absolute path.

getFilename is moved to a member function in GCOVProfiler so that it can access 
the GCOVOptions. This also removes the need for its VFS argument and the 
getVirtualFileSystem member function.

>From aa77838ad96ae0e8e9edac5c4ba007d7a4fca889 Mon Sep 17 00:00:00 2001
From: Nicolas Guichard <[email protected]>
Date: Wed, 7 Oct 2026 09:33:10 +0200
Subject: [PATCH] [gcov] Add support for -fprofile-abs-path

GCC has a -fprofile-abs-path option to use absolute paths to source
files in GCNO files. Citing GCC's documentation:
> Automatically convert relative source file names to absolute path
> names in the .gcno files. This allows gcov to find the correct
> sources in projects where compilations occur with different working
> directories.

This is particularly useful when doing unity builds where a generated
source file includes many original source files, as otherwise the
GCNO refers to the original source files by relative paths.

When the -fprofile-abs-path flag is passed to clang/clang++, the new
-coverage-notes-abs-paths flag is passed to cc1, which calls
vfs::FileSystem::makeAbsolute to get the absolute path.

getFilename is moved to a member function in GCOVProfiler so that it
can access the GCOVOptions. This also removes the need for its VFS
argument and the getVirtualFileSystem member function.
---
 clang/include/clang/Basic/CodeGenOptions.h    |  3 ++
 clang/include/clang/Options/Options.td        |  6 ++++
 clang/lib/CodeGen/BackendUtil.cpp             |  1 +
 clang/lib/Driver/ToolChains/Clang.cpp         |  6 ++++
 .../CodeGen/code-coverage-absolute-paths.c    | 13 +++++++++
 clang/test/Driver/coverage.c                  |  8 +++++
 .../llvm/Transforms/Utils/Instrumentation.h   |  3 ++
 .../Instrumentation/GCOVProfiling.cpp         | 29 ++++++++++++-------
 8 files changed, 58 insertions(+), 11 deletions(-)
 create mode 100644 clang/test/CodeGen/code-coverage-absolute-paths.c

diff --git a/clang/include/clang/Basic/CodeGenOptions.h 
b/clang/include/clang/Basic/CodeGenOptions.h
index c3a23415c922a..99a76b4edbf0e 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -292,6 +292,9 @@ class CodeGenOptions : public CodeGenOptionsBase {
   /// The filename with path we use for coverage notes files.
   std::string CoverageNotesFile;
 
+  /// Whether to force absolute paths in coverage notes files.
+  bool UseAbsolutePathsInNotesFile;
+
   /// Regexes separated by a semi-colon to filter the files to instrument.
   std::string ProfileFilterFiles;
 
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 88fbd72f4eae5..82aba4d4da4ff 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -2020,6 +2020,10 @@ def fno_profile_use : Flag<["-"], "fno-profile-use">,
 def ftest_coverage : Flag<["-"], "ftest-coverage">, Group<f_Group>,
     HelpText<"Produce gcov notes files (*.gcno)">;
 def fno_test_coverage : Flag<["-"], "fno-test-coverage">, Group<f_Group>;
+def fprofile_abs_path : Flag<["-"], "fprofile-abs-path">,
+    Group<f_Group>, Visibility<[ClangOption, CLOption]>,
+    HelpText<"Use absolute paths in gcov notes files (*.gcno)">;
+def fno_profile_abs_path : Flag<["-"], "fno-profile-abs-path">, Group<f_Group>;
 def fprofile_arcs : Flag<["-"], "fprofile-arcs">, Group<f_Group>,
     HelpText<"Instrument code to produce gcov data files (*.gcda)">;
 def fno_profile_arcs : Flag<["-"], "fno-profile-arcs">, Group<f_Group>;
@@ -8246,6 +8250,8 @@ def : Joined<["-"], "coverage-data-file=">,
   MarshallingInfoString<CodeGenOpts<"CoverageDataFile">>;
 def : Joined<["-"], "coverage-notes-file=">,
   MarshallingInfoString<CodeGenOpts<"CoverageNotesFile">>;
+def : Joined<["-"], "coverage-notes-abs-paths">,
+  MarshallingInfoFlag<CodeGenOpts<"UseAbsolutePathsInNotesFile">>;
 def coverage_version_EQ : Joined<["-"], "coverage-version=">,
   HelpText<"Four-byte version string for gcov files.">;
 def dump_coverage_mapping : Flag<["-"], "dump-coverage-mapping">,
diff --git a/clang/lib/CodeGen/BackendUtil.cpp 
b/clang/lib/CodeGen/BackendUtil.cpp
index c754c75aa59de..6ba8ebd2bc4d8 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -527,6 +527,7 @@ getGCOVOptions(const CodeGenOptions &CodeGenOpts, const 
LangOptions &LangOpts) {
   // LLVM's -default-gcov-version flag is set to something invalid.
   GCOVOptions Options;
   Options.EmitNotes = !CodeGenOpts.CoverageNotesFile.empty();
+  Options.UseAbsolutePathsInNotes = CodeGenOpts.UseAbsolutePathsInNotesFile;
   Options.EmitData = !CodeGenOpts.CoverageDataFile.empty();
   llvm::copy(CodeGenOpts.CoverageVersion, std::begin(Options.Version));
   Options.NoRedZone = CodeGenOpts.DisableRedZone;
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 63e7c42549d3f..df29f8d9c98f0 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -688,6 +688,12 @@ static void addPGOAndCoverageFlags(const ToolChain &TC, 
Compilation &C,
     if (EmitCovNotes) {
       CmdArgs.push_back(
           Args.MakeArgString("-coverage-notes-file=" + CoverageFilename));
+
+      bool UseAbsolutePathsInNotes =
+          Args.hasFlag(options::OPT_fprofile_abs_path,
+                       options::OPT_fno_profile_abs_path, false);
+      if (UseAbsolutePathsInNotes)
+        CmdArgs.push_back(Args.MakeArgString("-coverage-notes-abs-paths"));
     }
 
     if (EmitCovData) {
diff --git a/clang/test/CodeGen/code-coverage-absolute-paths.c 
b/clang/test/CodeGen/code-coverage-absolute-paths.c
new file mode 100644
index 0000000000000..839fcda8774d5
--- /dev/null
+++ b/clang/test/CodeGen/code-coverage-absolute-paths.c
@@ -0,0 +1,13 @@
+// RUN: rm -rf %t && mkdir %t
+// RUN: cd %S
+
+// RUN: %clang_cc1 -emit-llvm-bc -o /dev/null 
-coverage-notes-file=%t/notes-relative.gcno %{s:basename}
+// RUN: FileCheck --check-prefix=RELATIVE %s --input-file 
%t/notes-relative.gcno -DABSOLUTE_PATH=%s
+
+// RUN: %clang_cc1 -emit-llvm-bc -o /dev/null 
-coverage-notes-file=%t/notes-absolute.gcno -coverage-notes-abs-paths 
%{s:basename}
+// RUN: FileCheck --check-prefix=ABSOLUTE %s --input-file 
%t/notes-absolute.gcno -DABSOLUTE_PATH=%s
+
+void test() {}
+
+// RELATIVE-NOT: [[ABSOLUTE_PATH]]
+// ABSOLUTE: [[ABSOLUTE_PATH]]
diff --git a/clang/test/Driver/coverage.c b/clang/test/Driver/coverage.c
index ab791ada2d351..4aee8720470d4 100644
--- a/clang/test/Driver/coverage.c
+++ b/clang/test/Driver/coverage.c
@@ -32,6 +32,14 @@
 
 // PROFILE-DIR: "-coverage-data-file=abc
 
+/// Test -fprofile-abs-path
+// RUN: %clang -### -S -ftest-coverage %s 2>&1 | FileCheck 
--check-prefix=NO-PROFILE-ABS-PATH %s
+// RUN: %clang -### -S -fprofile-abs-path %s 2>&1 | FileCheck 
--check-prefix=NO-PROFILE-ABS-PATH %s
+// RUN: %clang -### -S -ftest-coverage -fprofile-abs-path %s 2>&1 | FileCheck 
--check-prefix=PROFILE-ABS-PATH %s
+
+// NO-PROFILE-ABS-PATH-NOT: "-coverage-notes-abs-paths"
+// PROFILE-ABS-PATH: "-coverage-notes-abs-paths"
+
 /// These should only get passed if any of --coverage, -ftest-coverage, or
 /// -fprofile-arcs is passed.
 // RUN: %clang -### -c %s 2>&1 | FileCheck --check-prefix=NO-COV %s
diff --git a/llvm/include/llvm/Transforms/Utils/Instrumentation.h 
b/llvm/include/llvm/Transforms/Utils/Instrumentation.h
index 8405245b2bc0a..e8279ae58e2cb 100644
--- a/llvm/include/llvm/Transforms/Utils/Instrumentation.h
+++ b/llvm/include/llvm/Transforms/Utils/Instrumentation.h
@@ -67,6 +67,9 @@ struct GCOVOptions {
   // Specify whether to emit .gcno files.
   bool EmitNotes;
 
+  // Specify whether to force absolute paths in coverage notes files.
+  bool UseAbsolutePathsInNotes;
+
   // Specify whether to modify the program to emit .gcda files when run.
   bool EmitData;
 
diff --git a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp 
b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
index 78b5dcf21cd03..0c96adddc72cd 100644
--- a/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
+++ b/llvm/lib/Transforms/Instrumentation/GCOVProfiling.cpp
@@ -76,6 +76,7 @@ static unsigned wordsOfString(StringRef s) {
 GCOVOptions GCOVOptions::getDefault() {
   GCOVOptions Options;
   Options.EmitNotes = true;
+  Options.UseAbsolutePathsInNotes = false;
   Options.EmitData = true;
   Options.NoRedZone = false;
   Options.Atomic = AtomicCounter;
@@ -113,7 +114,7 @@ class GCOVProfiler {
     os->write_zeros(4 - s.size() % 4);
   }
   void writeBytes(const char *Bytes, int Size) { os->write(Bytes, Size); }
-  vfs::FileSystem &getVirtualFileSystem() const { return VFS; }
+  SmallString<128> getFilename(const DIScope *SP);
 
 private:
   // Create the .gcno files for the Module based on DebugInfo.
@@ -216,16 +217,22 @@ static StringRef getFunctionName(const DISubprogram *SP) {
 
 /// Extract a filename for a DIScope.
 ///
-/// Prefer relative paths in the coverage notes. Clang also may split
-/// up absolute paths into a directory and filename component. When
-/// the relative path doesn't exist, reconstruct the absolute path.
-static SmallString<128> getFilename(const DIScope *SP, vfs::FileSystem &VFS) {
+/// Unless -fprofile-abs-path has been passed, prefer relative paths in the
+/// coverage notes. Clang also may split up absolute paths into a directory and
+/// filename component. When the relative path doesn't exist, reconstruct the
+/// absolute path.
+SmallString<128> GCOVProfiler::getFilename(const DIScope *SP) {
   SmallString<128> Path;
   StringRef RelPath = SP->getFilename();
-  if (VFS.exists(RelPath))
+  if (VFS.exists(RelPath)) {
     Path = RelPath;
-  else
+    if (Options.UseAbsolutePathsInNotes) {
+      VFS.makeAbsolute(Path);
+      sys::path::remove_dots(Path, true);
+    }
+  } else {
     sys::path::append(Path, SP->getDirectory(), SP->getFilename());
+  }
   return Path;
 }
 
@@ -362,7 +369,7 @@ namespace {
 
     void writeOut(uint32_t CfgChecksum) {
       write(GCOV_TAG_FUNCTION);
-      SmallString<128> Filename = getFilename(SP, P->getVirtualFileSystem());
+      SmallString<128> Filename = P->getFilename(SP);
       uint32_t BlockLen = 3 + wordsOfString(getFunctionName(SP));
       BlockLen += 1 + wordsOfString(Filename) + 4;
 
@@ -460,7 +467,7 @@ bool GCOVProfiler::isFunctionInstrumented(const Function 
&F) {
   if (FilterRe.empty() && ExcludeRe.empty()) {
     return true;
   }
-  SmallString<128> Filename = getFilename(F.getSubprogram(), VFS);
+  SmallString<128> Filename = getFilename(F.getSubprogram());
   auto It = InstrumentedFiles.find(Filename);
   if (It != InstrumentedFiles.end()) {
     return It->second;
@@ -795,7 +802,7 @@ bool GCOVProfiler::emitProfileNotes(
       // Add the function line number to the lines of the entry block
       // to have a counter for the function definition.
       uint32_t Line = SP->getLine();
-      auto Filename = getFilename(SP, VFS);
+      auto Filename = getFilename(SP);
 
       BranchProbabilityInfo *BPI = GetBPI(F);
       BlockFrequencyInfo *BFI = GetBFI(F);
@@ -887,7 +894,7 @@ bool GCOVProfiler::emitProfileNotes(
           if (SP != getDISubprogram(Scope))
             continue;
 
-          GCOVLines &Lines = Block.getFile(getFilename(Loc->getScope(), VFS));
+          GCOVLines &Lines = Block.getFile(getFilename(Loc->getScope()));
           Lines.addLine(Loc.getLine());
         }
         Line = 0;

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to