[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