[llvm] [Coverage] Fix atomic mode for certain types of coverage counter updates (PR #204597)

Roman Beliaev via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 19 03:18:05 PDT 2026


https://github.com/belyaevrd updated https://github.com/llvm/llvm-project/pull/204597

>From 9a7f57dc4d33ad31dda458af87a8117b45041c6c Mon Sep 17 00:00:00 2001
From: Roman Beliaev <r.beliaev at ispras.ru>
Date: Tue, 16 Jun 2026 18:03:36 +0300
Subject: [PATCH] [Coverage] Fix atomic mode for some types of coverage
 counters updates

In atomic mode, to prevent data races we need to use only atomic stores and
loads to access the coverage counters. This is also necessary to suppress TSan
diagnostics regarding racy MC/DC counter updates when `-fsanitize=thread` is
specified.

According to the C++ Standard, a program containing concurrent access to data,
where at least one access is a write and at least one is non-atomic, has a data
race, which results in UB. Therefore, it is important to use atomic operations
for updating single-byte coverage counters instead of plain load and store
operations. Moreover, in most cases this does not result in the generation of
slower instructions.
---
 llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp | 6 +++++-
 llvm/test/Instrumentation/InstrProfiling/mcdc.ll       | 4 +++-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp
index 57bc64acdca46..2ab9de6cfe2ff 100644
--- a/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp
+++ b/llvm/lib/Transforms/Instrumentation/InstrProfiling.cpp
@@ -1253,7 +1253,9 @@ void InstrLowerer::lowerCover(InstrProfCoverInst *CoverInstruction) {
   }
 
   // We store zero to represent that this block is covered.
-  Builder.CreateStore(Builder.getInt8(0), Addr);
+  StoreInst *Store = Builder.CreateStore(Builder.getInt8(0), Addr);
+  if (Options.Atomic || AtomicCounterUpdateAll)
+    Store->setOrdering(llvm::AtomicOrdering::Monotonic);
   CoverInstruction->eraseFromParent();
 }
 
@@ -1366,6 +1368,8 @@ void InstrLowerer::lowerMCDCTestVectorBitmapUpdate(
     // If ((Bitmap & Val) != Val), then execute atomic (Bitmap |= Val).
     // Note, just-loaded Bitmap might not be up-to-date. Use it just for
     // early testing.
+    // Use an atomic load to avoid data races.
+    Bitmap->setOrdering(llvm::AtomicOrdering::Monotonic);
     auto *Masked = Builder.CreateAnd(Bitmap, ShiftedVal);
     auto *ShouldStore = Builder.CreateICmpNE(Masked, ShiftedVal);
 
diff --git a/llvm/test/Instrumentation/InstrProfiling/mcdc.ll b/llvm/test/Instrumentation/InstrProfiling/mcdc.ll
index ee294a96b04ba..f00fea0396df3 100644
--- a/llvm/test/Instrumentation/InstrProfiling/mcdc.ll
+++ b/llvm/test/Instrumentation/InstrProfiling/mcdc.ll
@@ -36,7 +36,9 @@ entry:
   ; CHECK-NEXT: %[[LAB8:[0-9]+]] = and i32 %[[TEMP]], 7
   ; CHECK-NEXT: %[[LAB9:[0-9]+]] = trunc i32 %[[LAB8]] to i8
   ; CHECK-NEXT: %[[LAB10:[0-9]+]] = shl i8 1, %[[LAB9]]
-  ; CHECK-NEXT: %[[BITS:.+]] = load i8, ptr %[[LAB7]], align 1
+  ; BASIC-NEXT: %[[BITS:.+]] = load i8, ptr %[[LAB7]], align 1
+  ; RELOC-NEXT: %[[BITS:.+]] = load i8, ptr %[[LAB7]], align 1
+  ; ATOMIC-NEXT: %[[BITS:.+]] = load atomic i8, ptr %[[LAB7]] monotonic, align 1
   ; ATOMIC-NEXT: %[[MASKED:.+]] = and i8 %[[BITS]], %[[LAB10]]
   ; ATOMIC-NEXT: %[[SHOULDWRITE:.+]] = icmp ne i8 %[[MASKED]], %[[LAB10]]
   ; ATOMIC-NEXT: br i1 %[[SHOULDWRITE]], label %[[WRITE:.+]], label %[[SKIP:.+]], !prof ![[MDPROF:[0-9]+]]



More information about the llvm-commits mailing list