[llvm] [SanitizerCoverage] Don't create a comdat for unnamed functions (PR #228866)

Divyansh Yadav via llvm-commits llvm-commits at lists.llvm.org
Sun Oct 4 02:30:09 PDT 2026


https://github.com/schizophrenicmaniac created https://github.com/llvm/llvm-project/pull/228866

CreateFunctionLocalArrayInSection calls getOrCreateFunctionComdat for the function being instrumented, which names the comdat after the function. For an unnamed function (e.g. the private function MergeFunctions creates when it merges interposable functions), this trips the `F.hasName()` assertion. Without assertions it creates a comdat with an empty name: the printed IR can't be parsed back, no ELF group is emitted, and ld.lld --gc-sections ends up dropping the function's __sancov_pcs while keeping its counters, so libFuzzer refuses to start.

This skips creating a comdat for unnamed functions. Their arrays then take the existing no-comdat path and are added to llvm.used, so the counters and the PC table are kept together. An unnamed function that's already in a comdat keeps using it.

Fixes #228820

>From 256126d4e586635cf963868cb308bf42fb6af03e Mon Sep 17 00:00:00 2001
From: Divyansh Yadav <anshmcs at gmail.com>
Date: Sun, 4 Oct 2026 14:48:42 +0530
Subject: [PATCH] [SanitizerCoverage] Don't create a comdat for unnamed
 functions

---
 .../Instrumentation/SanitizerCoverage.cpp     |  7 ++--
 .../SanitizerCoverage/unnamed-function.ll     | 36 +++++++++++++++++++
 2 files changed, 41 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/Instrumentation/SanitizerCoverage/unnamed-function.ll

diff --git a/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp b/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp
index f2e2441c4b1bc..30fa16bcff492 100644
--- a/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp
+++ b/llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp
@@ -776,9 +776,12 @@ GlobalVariable *ModuleSanitizerCoverage::CreateFunctionLocalArrayInSection(
       Constant::getNullValue(ArrayTy), "__sancov_gen_");
 
   // noipa doesn't affect linkage, so it is ignored when deciding on a comdat.
+  // An unnamed function (e.g. one created by MergeFunctions) can't get a comdat
+  // named after it, so its arrays are retained through llvm.used instead.
   if (TargetTriple.supportsCOMDAT() &&
-      (F.hasComdat() || TargetTriple.isOSBinFormatELF() ||
-       !F.isInterposable(/*CheckNoIPA=*/false)))
+      (F.hasComdat() ||
+       (F.hasName() && (TargetTriple.isOSBinFormatELF() ||
+                        !F.isInterposable(/*CheckNoIPA=*/false)))))
     if (auto Comdat = getOrCreateFunctionComdat(F, TargetTriple))
       Array->setComdat(Comdat);
   Array->setSection(getSectionName(Section));
diff --git a/llvm/test/Instrumentation/SanitizerCoverage/unnamed-function.ll b/llvm/test/Instrumentation/SanitizerCoverage/unnamed-function.ll
new file mode 100644
index 0000000000000..5a98b75c9c613
--- /dev/null
+++ b/llvm/test/Instrumentation/SanitizerCoverage/unnamed-function.ll
@@ -0,0 +1,36 @@
+; Test that the arrays of an unnamed function are not put in a comdat named
+; after it (there is no name to use), and are retained through llvm.used.
+; An unnamed function that is already in a comdat keeps using it.
+; RUN: opt < %s -passes='module(sancov-module)' -sanitizer-coverage-level=1 -sanitizer-coverage-inline-8bit-counters -sanitizer-coverage-pc-table -mtriple x86_64-linux-gnu -S | FileCheck %s
+; RUN: opt < %s -passes='module(sancov-module)' -sanitizer-coverage-level=1 -sanitizer-coverage-inline-8bit-counters -sanitizer-coverage-pc-table -mtriple x86_64-windows-msvc -S | FileCheck %s
+
+$Comdat = comdat any
+
+define void @Named() {
+entry:
+  ret void
+}
+
+define private void @0() {
+entry:
+  ret void
+}
+
+define private void @1() comdat($Comdat) {
+entry:
+  ret void
+}
+
+; CHECK-NOT:  $ = comdat
+; CHECK:      @__sancov_gen_ = private global [1 x i8] zeroinitializer, section "{{[^"]*}}", comdat($Named), align 1{{$}}
+; CHECK-NEXT: @__sancov_gen_.1 = private constant [2 x ptr] [ptr @Named, ptr inttoptr (i64 1 to ptr)], section "{{[^"]*}}", comdat($Named), align 8{{$}}
+; CHECK-NEXT: @__sancov_gen_.2 = private global [1 x i8] zeroinitializer, section "{{[^"]*}}", align 1{{$}}
+; CHECK-NEXT: @__sancov_gen_.3 = private constant [2 x ptr] [ptr @0, ptr inttoptr (i64 1 to ptr)], section "{{[^"]*}}", align 8{{$}}
+; CHECK-NEXT: @__sancov_gen_.4 = private global [1 x i8] zeroinitializer, section "{{[^"]*}}", comdat($Comdat), align 1{{$}}
+; CHECK-NEXT: @__sancov_gen_.5 = private constant [2 x ptr] [ptr @1, ptr inttoptr (i64 1 to ptr)], section "{{[^"]*}}", comdat($Comdat), align 8{{$}}
+; CHECK:      @llvm.used = appending global [{{[0-9]+}} x ptr] [{{.*}}ptr @__sancov_gen_.2, ptr @__sancov_gen_.3{{.*}}], section "llvm.metadata"
+; CHECK:      @llvm.compiler.used = appending global [4 x ptr] [ptr @__sancov_gen_, ptr @__sancov_gen_.1, ptr @__sancov_gen_.4, ptr @__sancov_gen_.5], section "llvm.metadata"
+
+; CHECK: define void @Named() comdat {
+; CHECK: define private void @0() {
+; CHECK: define private void @1() comdat($Comdat) {



More information about the llvm-commits mailing list