[lld] [Coverage][WebAssembly] Discard `__llvm_covfun` and `__llvm_covmap` if `__llvm_prf_names` was discarded (PR #172023)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Dec 19 06:58:44 PST 2025
https://github.com/Spxg updated https://github.com/llvm/llvm-project/pull/172023
>From 89687d8a5dff8fbb2b4edc5514b61913900ef310 Mon Sep 17 00:00:00 2001
From: Spxg <unsafe at outlook.es>
Date: Fri, 19 Dec 2025 22:56:10 +0800
Subject: [PATCH] [Coverage][WebAssembly] Discard `__llvm_covfun` and
`__llvm_covmap` if `__llvm_prf_names` was discarded
---
lld/test/wasm/Inputs/malformed-prf1.ll | 48 ++++++++++++++++++++++++++
lld/test/wasm/Inputs/malformed-prf2.ll | 44 +++++++++++++++++++++++
lld/test/wasm/custom-section-align.s | 16 ---------
lld/test/wasm/malformed-prf.ll | 28 +++++++++++++++
lld/wasm/InputFiles.h | 2 ++
lld/wasm/Writer.cpp | 9 +++++
6 files changed, 131 insertions(+), 16 deletions(-)
create mode 100644 lld/test/wasm/Inputs/malformed-prf1.ll
create mode 100644 lld/test/wasm/Inputs/malformed-prf2.ll
create mode 100644 lld/test/wasm/malformed-prf.ll
diff --git a/lld/test/wasm/Inputs/malformed-prf1.ll b/lld/test/wasm/Inputs/malformed-prf1.ll
new file mode 100644
index 0000000000000..9151f922dec23
--- /dev/null
+++ b/lld/test/wasm/Inputs/malformed-prf1.ll
@@ -0,0 +1,48 @@
+; ModuleID = 'malformed-prf1.c'
+source_filename = "malformed-prf1.c"
+target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
+target triple = "wasm32-unknown-unknown"
+
+$__llvm_profile_runtime_user = comdat any
+
+$__covrec_5CF8C24CDB18BDACu = comdat any
+
+ at __covrec_5CF8C24CDB18BDACu = linkonce_odr hidden constant <{ i64, i32, i64, i64, [9 x i8] }> <{ i64 6699318081062747564, i32 9, i64 0, i64 -8255375002284483420, [9 x i8] c"\01\01\00\01\01\03\0C\02\02" }>, section "__llvm_covfun", comdat, align 8
+ at __llvm_coverage_mapping = private constant { { i32, i32, i32, i32 }, [76 x i8] } { { i32, i32, i32, i32 } { i32 0, i32 76, i32 0, i32 6 }, [76 x i8] c"\02EIx\DA\0D\CA\D1\09\800\0C\05\C0\15\\D\1F\E2\14\8E\11\DA\94\22MS\92T\D7\B7\9F\07w\A1\AA0f%\F3\0E\E3\A1h\ED\95}\98>\9Cb!#\D8\03\1F\B9\E0\EEc\86oB\AD\A8\09\E7\D5\CAy\A4\1F\85E\19\B0" }, section "__llvm_covmap", align 8
+ at __llvm_profile_runtime = external hidden global i32
+ at __profc_foo = private global [1 x i64] zeroinitializer, section "__llvm_prf_cnts", align 8
+ at __profd_foo = private global { i64, i64, i32, i32, ptr, ptr, i32, [3 x i16], i32 } { i64 6699318081062747564, i64 0, i32 sub (i32 ptrtoint (ptr @__profc_foo to i32), i32 ptrtoint (ptr @__profd_foo to i32)), i32 0, ptr null, ptr null, i32 1, [3 x i16] zeroinitializer, i32 0 }, section "__llvm_prf_data", align 8
+ at __llvm_prf_nm = private constant [13 x i8] c"\03\0Bx\DAK\CB\CF\07\00\02\82\01E", section "__llvm_prf_names", align 1
+ at llvm.used = appending global [5 x ptr] [ptr @__covrec_5CF8C24CDB18BDACu, ptr @__llvm_coverage_mapping, ptr @__llvm_profile_runtime_user, ptr @__profd_foo, ptr @__llvm_prf_nm], section "llvm.metadata"
+
+; Function Attrs: noinline nounwind optnone
+define hidden void @foo() #0 {
+ %1 = load i64, ptr @__profc_foo, align 8
+ %2 = add i64 %1, 1
+ store i64 %2, ptr @__profc_foo, align 8
+ call void @bar()
+ ret void
+}
+
+; Function Attrs: nounwind
+declare void @llvm.instrprof.increment(ptr, i64, i32, i32) #1
+
+declare void @bar(...) #2
+
+; Function Attrs: noinline
+define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #3 comdat {
+ %1 = load i32, ptr @__llvm_profile_runtime, align 4
+ ret i32 %1
+}
+
+attributes #0 = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+multivalue,+mutable-globals,+nontrapping-fptoint,+reference-types,+sign-ext" }
+attributes #1 = { nounwind }
+attributes #2 = { "no-prototype" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+multivalue,+mutable-globals,+nontrapping-fptoint,+reference-types,+sign-ext" }
+attributes #3 = { noinline }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+
+!0 = !{i32 2, !"EnableValueProfiling", i32 0}
+!1 = !{i32 1, !"wchar_size", i32 4}
+!2 = !{!"clang version 21.1.6"}
diff --git a/lld/test/wasm/Inputs/malformed-prf2.ll b/lld/test/wasm/Inputs/malformed-prf2.ll
new file mode 100644
index 0000000000000..9831e17c7c862
--- /dev/null
+++ b/lld/test/wasm/Inputs/malformed-prf2.ll
@@ -0,0 +1,44 @@
+; ModuleID = 'malformed-prf2.c'
+source_filename = "malformed-prf2.c"
+target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
+target triple = "wasm32-unknown-unknown"
+
+$__llvm_profile_runtime_user = comdat any
+
+$__covrec_E413754A191DB537u = comdat any
+
+ at __covrec_E413754A191DB537u = linkonce_odr hidden constant <{ i64, i32, i64, i64, [9 x i8] }> <{ i64 -2012135647395072713, i32 9, i64 0, i64 -3975376370493289617, [9 x i8] c"\01\01\00\01\01\01\0C\00\0E" }>, section "__llvm_covfun", comdat, align 8
+ at __llvm_coverage_mapping = private constant { { i32, i32, i32, i32 }, [76 x i8] } { { i32, i32, i32, i32 } { i32 0, i32 76, i32 0, i32 6 }, [76 x i8] c"\02EIx\DA\0D\CA\D1\09\800\0C\05\C0\15\\D\1F\E8\14\8E\11\DA\94\22MS\92T\D7\B7\9F\07w\A1\AA0f%\F3\0E\E3\A1h\ED\95}\98>\9Cb!#\D8\03\1F\B9\E0\EEc\86oB\AD\A8\09\E7\D5\CAy\A4\1F\85H\19\B1" }, section "__llvm_covmap", align 8
+ at __llvm_profile_runtime = external hidden global i32
+ at __profc_bar = private global [1 x i64] zeroinitializer, section "__llvm_prf_cnts", align 8
+ at __profd_bar = private global { i64, i64, i32, i32, ptr, ptr, i32, [3 x i16], i32 } { i64 -2012135647395072713, i64 0, i32 sub (i32 ptrtoint (ptr @__profc_bar to i32), i32 ptrtoint (ptr @__profd_bar to i32)), i32 0, ptr null, ptr null, i32 1, [3 x i16] zeroinitializer, i32 0 }, section "__llvm_prf_data", align 8
+ at __llvm_prf_nm = private constant [13 x i8] c"\03\0Bx\DAKJ,\02\00\02]\016", section "__llvm_prf_names", align 1
+ at llvm.used = appending global [5 x ptr] [ptr @__covrec_E413754A191DB537u, ptr @__llvm_coverage_mapping, ptr @__llvm_profile_runtime_user, ptr @__profd_bar, ptr @__llvm_prf_nm], section "llvm.metadata"
+
+; Function Attrs: noinline nounwind optnone
+define hidden void @bar() #0 {
+ %1 = load i64, ptr @__profc_bar, align 8
+ %2 = add i64 %1, 1
+ store i64 %2, ptr @__profc_bar, align 8
+ ret void
+}
+
+; Function Attrs: nounwind
+declare void @llvm.instrprof.increment(ptr, i64, i32, i32) #1
+
+; Function Attrs: noinline
+define linkonce_odr hidden i32 @__llvm_profile_runtime_user() #2 comdat {
+ %1 = load i32, ptr @__llvm_profile_runtime, align 4
+ ret i32 %1
+}
+
+attributes #0 = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+multivalue,+mutable-globals,+nontrapping-fptoint,+reference-types,+sign-ext" }
+attributes #1 = { nounwind }
+attributes #2 = { noinline }
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+
+!0 = !{i32 2, !"EnableValueProfiling", i32 0}
+!1 = !{i32 1, !"wchar_size", i32 4}
+!2 = !{!"clang version 21.1.6"}
diff --git a/lld/test/wasm/custom-section-align.s b/lld/test/wasm/custom-section-align.s
index 0e46177f4cdb7..29fb250cf78e5 100644
--- a/lld/test/wasm/custom-section-align.s
+++ b/lld/test/wasm/custom-section-align.s
@@ -2,22 +2,6 @@
# RUN: wasm-ld --no-entry %t.o -o %t.wasm
# RUN: obj2yaml %t.wasm | FileCheck %s
-# Check that "__llvm_covfun" custom section is aligned to 8 bytes.
-
- .section .custom_section.__llvm_covfun,"GR",@,__covrec_A
- .int32 1
- .int8 2
-# pad .int8 0
-# .int8 0
-# .int8 0
-
- .section .custom_section.__llvm_covfun,"GR",@,__covrec_B
- .int32 3
-
-# CHECK: - Type: CUSTOM
-# CHECK-NEXT: Name: __llvm_covfun
-# CHECK-NEXT: Payload: '010000000200000003000000'
-
# Check that regular custom sections are not aligned.
.section .custom_section.foo,"GR",@,foo_A
.int32 1
diff --git a/lld/test/wasm/malformed-prf.ll b/lld/test/wasm/malformed-prf.ll
new file mode 100644
index 0000000000000..77c6f7add5c3d
--- /dev/null
+++ b/lld/test/wasm/malformed-prf.ll
@@ -0,0 +1,28 @@
+; RUN: llc -filetype=obj %s -o %t.instr-prof.o
+; RUN: llc -filetype=obj %p/Inputs/malformed-prf1.ll -o %t.malformed-prf1.o
+; RUN: llc -filetype=obj %p/Inputs/malformed-prf2.ll -o %t.malformed-prf2.o
+; RUN: wasm-ld -o %t.wasm %t.instr-prof.o %t.malformed-prf1.o --start-lib %t.malformed-prf2.o --end-lib --gc-sections --no-entry
+; RUN: llvm-cov export --object %t.wasm --empty-profile
+
+; 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.
+;
+; WASM stores `__llvm_covfun` in custom section, while `__llvm_prf_names` is stored in the DATA section.
+; The former not be GC, whereas the latter may be GC, causing llvm-cov execution to fail.
+;
+; Now, __llvm_covfun and __llvm_covmap will be discarded if __llvm_prf_names was discarded and making llvm-cov work.
+
+; ModuleID = 'instr-prof.c'
+source_filename = "instr-prof.c"
+target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20"
+target triple = "wasm32-unknown-unknown"
+
+ at __llvm_profile_runtime = hidden global i32 0, align 4
+
+!llvm.module.flags = !{!0, !1}
+!llvm.ident = !{!2}
+
+!0 = !{i32 2, !"EnableValueProfiling", i32 0}
+!1 = !{i32 1, !"wchar_size", i32 4}
+!2 = !{!"clang version 21.1.6"}
+
diff --git a/lld/wasm/InputFiles.h b/lld/wasm/InputFiles.h
index fd7fcb13f4426..aea15908f16ec 100644
--- a/lld/wasm/InputFiles.h
+++ b/lld/wasm/InputFiles.h
@@ -121,6 +121,8 @@ class ObjFile : public WasmFileBase {
// Maps input type indices to output type indices
std::vector<uint32_t> typeMap;
std::vector<bool> typeIsUsed;
+ // Check if the __llvm_prf_names segment is retained
+ bool prfSegmentsRetained;
// Maps function indices to table indices
std::vector<uint32_t> tableEntries;
std::vector<uint32_t> tableEntriesRel;
diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp
index 9a5b56fc52e2f..42fdc97335d7b 100644
--- a/lld/wasm/Writer.cpp
+++ b/lld/wasm/Writer.cpp
@@ -26,6 +26,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/BinaryFormat/Wasm.h"
+#include "llvm/ProfileData/InstrProf.h"
#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/Parallel.h"
@@ -153,6 +154,12 @@ void Writer::calculateCustomSections() {
// Strip debug section in that option was specified.
if (stripDebug && name.starts_with(".debug_"))
continue;
+ // Discard `__llvm_covfun` and `__llvm_covmap` sections if
+ // `__llvm_prf_names` segment have already been discarded.
+ if (!file->prfSegmentsRetained &&
+ (name == getInstrProfSectionName(IPSK_covfun, Triple::Wasm) ||
+ name == getInstrProfSectionName(IPSK_covmap, Triple::Wasm)))
+ continue;
// Otherwise include custom sections by default and concatenate their
// contents.
customSectionMapping[name].push_back(section);
@@ -1036,6 +1043,8 @@ void Writer::createOutputSegments() {
continue;
StringRef name = getOutputDataSegmentName(*segment);
OutputSegment *s = nullptr;
+ if (name == getInstrProfSectionName(IPSK_name, Triple::Wasm))
+ file->prfSegmentsRetained = true;
// When running in relocatable mode we can't merge segments that are part
// of comdat groups since the ultimate linker needs to be able exclude or
// include them individually.
More information about the llvm-commits
mailing list