[lld] [llvm] [Coverage][WebAssembly] Keep InstrProf segments during gc (PR #172023)

via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 12 07:39:23 PST 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lld-wasm

Author: None (Spxg)

<details>
<summary>Changes</summary>

While enabling coverage instrumentation for a Rust program, I encountered llvm-cov exiting with an error: "malformed instrumentation profile data: function name is empty".  I found that the Rust linker defaults to using `--gc-sections`. The issue is resolved by setting `--no-gc-sections`.

Every covfun record holds a hash of its symbol name, and `llvm-cov` will exit fatally if it can't resolve that hash back to an entry in the binary's `__llvm_prf_names` linker section.

However, WASM stores `__llvm_covfun` in `CustomSection`, while `__llvm_prf_names` is stored in the `DATA` section. The former cannot be GC, whereas the latter may be GC, causing `llvm-cov` execution to fail.

This flag records whether it is an `InstrProfSegment` so that it can be enqueued during GC.

---
Full diff: https://github.com/llvm/llvm-project/pull/172023.diff


4 Files Affected:

- (modified) lld/wasm/InputChunks.h (+3) 
- (modified) lld/wasm/InputFiles.cpp (+17) 
- (modified) lld/wasm/MarkLive.cpp (+12-1) 
- (modified) llvm/include/llvm/BinaryFormat/Wasm.h (+1) 


``````````diff
diff --git a/lld/wasm/InputChunks.h b/lld/wasm/InputChunks.h
index 1fe78d76631f1..7cc49da1064fc 100644
--- a/lld/wasm/InputChunks.h
+++ b/lld/wasm/InputChunks.h
@@ -83,6 +83,9 @@ class InputChunk {
 
   bool isTLS() const { return flags & llvm::wasm::WASM_SEG_FLAG_TLS; }
   bool isRetained() const { return flags & llvm::wasm::WASM_SEG_FLAG_RETAIN; }
+  bool isInstrProfSegment() const {
+    return flags & llvm::wasm::WASM_SEG_FLAG_PRF;
+  }
 
   ObjFile *file;
   OutputSection *outputSec = nullptr;
diff --git a/lld/wasm/InputFiles.cpp b/lld/wasm/InputFiles.cpp
index 387b5eb10ba2f..fedaa4b737e07 100644
--- a/lld/wasm/InputFiles.cpp
+++ b/lld/wasm/InputFiles.cpp
@@ -577,6 +577,23 @@ void ObjFile::parse(bool ignoreComdats) {
     if (!seg->isTLS() &&
         (seg->name.starts_with(".tdata") || seg->name.starts_with(".tbss")))
       seg->flags |= WASM_SEG_FLAG_TLS;
+
+    // Every covfun record holds a hash of its symbol name, and `llvm-cov`
+    // will exit fatally if it can't resolve that hash back to an entry in
+    // the binary's `__llvm_prf_names` linker section.
+    //
+    // However, WASM stores `__llvm_covfun` in `CustomSection`, while
+    // `__llvm_prf_names` is stored in the `DATA` section. The former cannot be
+    // GC, whereas the latter may be GC, causing `llvm-cov` execution to fail.
+    //
+    // This flag records whether it is an `InstrProfSegment` so that it can be
+    // enqueued during GC.
+    if (seg->name == getInstrProfSectionName(IPSK_name, Triple::Wasm) ||
+        seg->name == getInstrProfSectionName(IPSK_cnts, Triple::Wasm) ||
+        seg->name == getInstrProfSectionName(IPSK_data, Triple::Wasm)) {
+      seg->flags |= WASM_SEG_FLAG_PRF;
+    }
+
     segments.emplace_back(seg);
   }
   setRelocs(segments, dataSection);
diff --git a/lld/wasm/MarkLive.cpp b/lld/wasm/MarkLive.cpp
index 2b2cf19f14b30..c51214b6920cf 100644
--- a/lld/wasm/MarkLive.cpp
+++ b/lld/wasm/MarkLive.cpp
@@ -43,6 +43,7 @@ class MarkLive {
   void enqueue(InputChunk *chunk);
   void enqueueInitFunctions(const ObjFile *sym);
   void enqueueRetainedSegments(const ObjFile *file);
+  void enqueueInstrProfSegments(const ObjFile *file);
   void mark();
   bool isCallCtorsLive();
 
@@ -104,6 +105,13 @@ void MarkLive::enqueueRetainedSegments(const ObjFile *file) {
       enqueue(chunk);
 }
 
+// Mark instr profile segments.
+void MarkLive::enqueueInstrProfSegments(const ObjFile *file) {
+  for (InputChunk *chunk : file->segments)
+    if (chunk->isInstrProfSegment())
+      enqueue(chunk);
+}
+
 void MarkLive::run() {
   // Add GC root symbols.
   if (!ctx.arg.entry.empty())
@@ -117,7 +125,9 @@ void MarkLive::run() {
   if (ctx.sym.callDtors)
     enqueue(ctx.sym.callDtors);
 
-  for (const ObjFile *obj : ctx.objectFiles)
+  for (const ObjFile *obj : ctx.objectFiles) {
+    // Enqueue instr profile segments
+    enqueueInstrProfSegments(obj);
     if (obj->isLive()) {
       // Enqueue constructors in objects explicitly live from the command-line.
       enqueueInitFunctions(obj);
@@ -125,6 +135,7 @@ void MarkLive::run() {
       // command-line.
       enqueueRetainedSegments(obj);
     }
+  }
 
   mark();
 
diff --git a/llvm/include/llvm/BinaryFormat/Wasm.h b/llvm/include/llvm/BinaryFormat/Wasm.h
index cf90a43d0d7e8..1f76330a29efb 100644
--- a/llvm/include/llvm/BinaryFormat/Wasm.h
+++ b/llvm/include/llvm/BinaryFormat/Wasm.h
@@ -229,6 +229,7 @@ enum WasmSegmentFlag : unsigned {
   WASM_SEG_FLAG_STRINGS = 0x1,
   WASM_SEG_FLAG_TLS = 0x2,
   WASM_SEG_FLAG_RETAIN = 0x4,
+  WASM_SEG_FLAG_PRF = 0x8,
 };
 
 // Kinds of tag attributes.

``````````

</details>


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


More information about the llvm-commits mailing list