[llvm-branch-commits] [clang] [MC/DC] Create dedicated MCDCCondBitmapAddr for each Decision (PR #125411)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sun Feb 2 05:35:12 PST 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: NAKAMURA Takumi (chapuni)

<details>
<summary>Changes</summary>

MCDCCondBitmapAddr is moved from `CodeGenFunction` into `MCDCState` and created for each Decision.

In `maybeCreateMCDCCondBitmap`, Allocate bitmaps for all valid Decisions and emit them order by ID, to prevent nondeterminism.

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


6 Files Affected:

- (modified) clang/lib/CodeGen/CodeGenFunction.h (+9-8) 
- (modified) clang/lib/CodeGen/CodeGenPGO.cpp (+35-6) 
- (modified) clang/lib/CodeGen/CodeGenPGO.h (+3-5) 
- (modified) clang/lib/CodeGen/MCDCState.h (+2) 
- (added) clang/test/CoverageMapping/mcdc-single-cond.cpp (+101) 
- (modified) clang/test/Profile/c-mcdc-logicalop-ternary.c (+10-8) 


``````````diff
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index e978cad4336238..20d81ba3a3bde1 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -1628,9 +1628,6 @@ class CodeGenFunction : public CodeGenTypeCache {
 
   CodeGenPGO PGO;
 
-  /// Bitmap used by MC/DC to track condition outcomes of a boolean expression.
-  Address MCDCCondBitmapAddr = Address::invalid();
-
   /// Calculate branch weights appropriate for PGO data
   llvm::MDNode *createProfileWeights(uint64_t TrueCount,
                                      uint64_t FalseCount) const;
@@ -1669,8 +1666,12 @@ class CodeGenFunction : public CodeGenTypeCache {
   void maybeCreateMCDCCondBitmap() {
     if (isMCDCCoverageEnabled()) {
       PGO.emitMCDCParameters(Builder);
-      MCDCCondBitmapAddr =
-          CreateIRTemp(getContext().UnsignedIntTy, "mcdc.addr");
+
+      // Set up MCDCCondBitmapAddr for each Decision.
+      // Note: This doesn't initialize Addrs in invalidated Decisions.
+      for (auto *MCDCCondBitmapAddr : PGO.getMCDCCondBitmapAddrArray(Builder))
+        *MCDCCondBitmapAddr =
+            CreateIRTemp(getContext().UnsignedIntTy, "mcdc.addr");
     }
   }
 
@@ -1682,7 +1683,7 @@ class CodeGenFunction : public CodeGenTypeCache {
   /// Zero-init the MCDC temp value.
   void maybeResetMCDCCondBitmap(const Expr *E) {
     if (isMCDCCoverageEnabled() && isBinaryLogicalOp(E)) {
-      PGO.emitMCDCCondBitmapReset(Builder, E, MCDCCondBitmapAddr);
+      PGO.emitMCDCCondBitmapReset(Builder, E);
       PGO.setCurrentStmt(E);
     }
   }
@@ -1691,7 +1692,7 @@ class CodeGenFunction : public CodeGenTypeCache {
   /// If \p StepV is null, the default increment is 1.
   void maybeUpdateMCDCTestVectorBitmap(const Expr *E) {
     if (isMCDCCoverageEnabled() && isBinaryLogicalOp(E)) {
-      PGO.emitMCDCTestVectorBitmapUpdate(Builder, E, MCDCCondBitmapAddr, *this);
+      PGO.emitMCDCTestVectorBitmapUpdate(Builder, E, *this);
       PGO.setCurrentStmt(E);
     }
   }
@@ -1699,7 +1700,7 @@ class CodeGenFunction : public CodeGenTypeCache {
   /// Update the MCDC temp value with the condition's evaluated result.
   void maybeUpdateMCDCCondBitmap(const Expr *E, llvm::Value *Val) {
     if (isMCDCCoverageEnabled()) {
-      PGO.emitMCDCCondBitmapUpdate(Builder, E, MCDCCondBitmapAddr, Val, *this);
+      PGO.emitMCDCCondBitmapUpdate(Builder, E, Val, *this);
       PGO.setCurrentStmt(E);
     }
   }
diff --git a/clang/lib/CodeGen/CodeGenPGO.cpp b/clang/lib/CodeGen/CodeGenPGO.cpp
index 0331ff83e633f7..7d16f673ada419 100644
--- a/clang/lib/CodeGen/CodeGenPGO.cpp
+++ b/clang/lib/CodeGen/CodeGenPGO.cpp
@@ -1245,9 +1245,29 @@ void CodeGenPGO::emitMCDCParameters(CGBuilderTy &Builder) {
       CGM.getIntrinsic(llvm::Intrinsic::instrprof_mcdc_parameters), Args);
 }
 
+/// Fill mcdc.addr order by ID.
+std::vector<Address *>
+CodeGenPGO::getMCDCCondBitmapAddrArray(CGBuilderTy &Builder) {
+  std::vector<Address *> Result;
+
+  if (!canEmitMCDCCoverage(Builder) || !RegionMCDCState)
+    return Result;
+
+  SmallVector<std::pair<unsigned, Address *>> SortedPair;
+  for (auto &[_, V] : RegionMCDCState->DecisionByStmt)
+    if (V.isValid())
+      SortedPair.emplace_back(V.ID, &V.MCDCCondBitmapAddr);
+
+  llvm::sort(SortedPair);
+
+  for (auto &[_, MCDCCondBitmapAddr] : SortedPair)
+    Result.push_back(MCDCCondBitmapAddr);
+
+  return Result;
+}
+
 void CodeGenPGO::emitMCDCTestVectorBitmapUpdate(CGBuilderTy &Builder,
                                                 const Expr *S,
-                                                Address MCDCCondBitmapAddr,
                                                 CodeGenFunction &CGF) {
   if (!canEmitMCDCCoverage(Builder) || !RegionMCDCState)
     return;
@@ -1258,6 +1278,10 @@ void CodeGenPGO::emitMCDCTestVectorBitmapUpdate(CGBuilderTy &Builder,
   if (DecisionStateIter == RegionMCDCState->DecisionByStmt.end())
     return;
 
+  auto &MCDCCondBitmapAddr = DecisionStateIter->second.MCDCCondBitmapAddr;
+  if (!MCDCCondBitmapAddr.isValid())
+    return;
+
   // Don't create tvbitmap_update if the record is allocated but excluded.
   // Or `bitmap |= (1 << 0)` would be wrongly executed to the next bitmap.
   if (DecisionStateIter->second.Indices.size() == 0)
@@ -1280,14 +1304,16 @@ void CodeGenPGO::emitMCDCTestVectorBitmapUpdate(CGBuilderTy &Builder,
       CGM.getIntrinsic(llvm::Intrinsic::instrprof_mcdc_tvbitmap_update), Args);
 }
 
-void CodeGenPGO::emitMCDCCondBitmapReset(CGBuilderTy &Builder, const Expr *S,
-                                         Address MCDCCondBitmapAddr) {
+void CodeGenPGO::emitMCDCCondBitmapReset(CGBuilderTy &Builder, const Expr *S) {
   if (!canEmitMCDCCoverage(Builder) || !RegionMCDCState)
     return;
 
-  S = S->IgnoreParens();
+  auto I = RegionMCDCState->DecisionByStmt.find(S->IgnoreParens());
+  if (I == RegionMCDCState->DecisionByStmt.end())
+    return;
 
-  if (!RegionMCDCState->DecisionByStmt.contains(S))
+  auto &MCDCCondBitmapAddr = I->second.MCDCCondBitmapAddr;
+  if (!MCDCCondBitmapAddr.isValid())
     return;
 
   // Emit intrinsic that resets a dedicated temporary value on the stack to 0.
@@ -1295,7 +1321,6 @@ void CodeGenPGO::emitMCDCCondBitmapReset(CGBuilderTy &Builder, const Expr *S,
 }
 
 void CodeGenPGO::emitMCDCCondBitmapUpdate(CGBuilderTy &Builder, const Expr *S,
-                                          Address MCDCCondBitmapAddr,
                                           llvm::Value *Val,
                                           CodeGenFunction &CGF) {
   if (!canEmitMCDCCoverage(Builder) || !RegionMCDCState)
@@ -1325,6 +1350,10 @@ void CodeGenPGO::emitMCDCCondBitmapUpdate(CGBuilderTy &Builder, const Expr *S,
   if (DecisionIter == RegionMCDCState->DecisionByStmt.end())
     return;
 
+  auto &MCDCCondBitmapAddr = DecisionIter->second.MCDCCondBitmapAddr;
+  if (!MCDCCondBitmapAddr.isValid())
+    return;
+
   const auto &TVIdxs = DecisionIter->second.Indices[Branch.ID];
 
   auto *CurTV = Builder.CreateLoad(MCDCCondBitmapAddr,
diff --git a/clang/lib/CodeGen/CodeGenPGO.h b/clang/lib/CodeGen/CodeGenPGO.h
index 1944b640951d5c..0d7dd13f611a93 100644
--- a/clang/lib/CodeGen/CodeGenPGO.h
+++ b/clang/lib/CodeGen/CodeGenPGO.h
@@ -114,14 +114,12 @@ class CodeGenPGO {
   void emitCounterSetOrIncrement(CGBuilderTy &Builder, const Stmt *S,
                                  llvm::Value *StepV);
   void emitMCDCTestVectorBitmapUpdate(CGBuilderTy &Builder, const Expr *S,
-                                      Address MCDCCondBitmapAddr,
                                       CodeGenFunction &CGF);
   void emitMCDCParameters(CGBuilderTy &Builder);
-  void emitMCDCCondBitmapReset(CGBuilderTy &Builder, const Expr *S,
-                               Address MCDCCondBitmapAddr);
+  std::vector<Address *> getMCDCCondBitmapAddrArray(CGBuilderTy &Builder);
+  void emitMCDCCondBitmapReset(CGBuilderTy &Builder, const Expr *S);
   void emitMCDCCondBitmapUpdate(CGBuilderTy &Builder, const Expr *S,
-                                Address MCDCCondBitmapAddr, llvm::Value *Val,
-                                CodeGenFunction &CGF);
+                                llvm::Value *Val, CodeGenFunction &CGF);
 
   void markStmtAsUsed(bool Skipped, const Stmt *S) {
     // Do nothing.
diff --git a/clang/lib/CodeGen/MCDCState.h b/clang/lib/CodeGen/MCDCState.h
index 0b6f5f28235f43..2bb4eaf9fdfc83 100644
--- a/clang/lib/CodeGen/MCDCState.h
+++ b/clang/lib/CodeGen/MCDCState.h
@@ -13,6 +13,7 @@
 #ifndef LLVM_CLANG_LIB_CODEGEN_MCDCSTATE_H
 #define LLVM_CLANG_LIB_CODEGEN_MCDCSTATE_H
 
+#include "Address.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ProfileData/Coverage/MCDCTypes.h"
@@ -38,6 +39,7 @@ struct State {
     unsigned BitmapIdx;
     IndicesTy Indices;
     unsigned ID = InvalidID;
+    Address MCDCCondBitmapAddr = Address::invalid();
 
     bool isValid() const { return ID != InvalidID; }
 
diff --git a/clang/test/CoverageMapping/mcdc-single-cond.cpp b/clang/test/CoverageMapping/mcdc-single-cond.cpp
new file mode 100644
index 00000000000000..d6c694a63dd285
--- /dev/null
+++ b/clang/test/CoverageMapping/mcdc-single-cond.cpp
@@ -0,0 +1,101 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 -fcoverage-mcdc -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -disable-llvm-passes -emit-llvm -o %t2.ll %s | FileCheck %s --check-prefixes=MM,MM2
+// RUN: FileCheck %s --check-prefixes=LL,LL2 < %t2.ll
+
+// LL: define{{.+}}func_cond{{.+}}(
+// MM: func_cond{{.*}}:
+int func_cond(bool a, bool b) {
+  // %mcdc.addr* are emitted by static order.
+  // LL:   %[[MA4:mcdc.addr.*]] = alloca i32, align 4
+  // LL:   %[[MA6:mcdc.addr.*]] = alloca i32, align 4
+  // LL:   %[[MA7:mcdc.addr.*]] = alloca i32, align 4
+  // LL:   %[[MA9:mcdc.addr.*]] = alloca i32, align 4
+  // LL:   %[[MA10:mcdc.addr.*]] = alloca i32, align 4
+  // LL:  call void @llvm.instrprof.mcdc.parameters(ptr @[[PROFN:.+]], i64 [[#H:]], i32 [[#BS:]])
+  int count = 0;
+  if (a)
+    // NB=2 Single cond
+    // MM2-NOT: Decision
+    ++count;
+  if (a ? true : false)
+    // NB=2,2 Wider decision comes first.
+    // MA2 has C:2
+    // MA3 has C:1
+    ++count;
+  if (a && b ? true : false)
+    // NB=2,3 Wider decision comes first.
+    // MM2:  Decision,File 0, [[@LINE-2]]:7 -> [[#L:@LINE-2]]:13 = M:[[#I:3]], C:2
+    // MM:   Branch,File 0, [[#L]]:7 -> [[#L]]:8 = #6, (#0 - #6) [1,2,0]
+    // MM:   Branch,File 0, [[#L]]:12 -> [[#L]]:13 = #7, (#6 - #7) [2,0,0]
+    // LL:   store i32 0, ptr %[[MA4]], align 4
+    // LL:   = load i32, ptr %[[MA4]], align 4
+    // LL:   store i32 %{{.+}}, ptr %[[MA4]], align 4
+    // LL:   = load i32, ptr %[[MA4]], align 4
+    // LL:   store i32 %{{.+}}, ptr %[[MA4]], align 4
+    // LL2:  call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:0]], ptr %[[MA4]])
+    ++count;
+  while (a || true) {
+    // NB=3 BinOp only
+    // MM:   Decision,File 0, [[@LINE-2]]:10 -> [[#L:@LINE-2]]:19 = M:[[#I:I+3]], C:2
+    // MM:   Branch,File 0, [[#L]]:10 -> [[#L]]:11 = (#0 - #9), #9 [1,0,2]
+    // MM:   Branch,File 0, [[#L]]:15 -> [[#L]]:19 = (#9 - #10), 0 [2,0,0]
+    // LL:   store i32 0, ptr %[[MA6]], align 4
+    // LL:   = load i32, ptr %[[MA6]], align 4
+    // LL:   store i32 %{{.+}}, ptr %[[MA6]], align 4
+    // LL:   = load i32, ptr %[[MA6]], align 4
+    // LL:   store i32 %{{.+}}, ptr %[[MA6]], align 4
+    // LL2:  call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:B+3]], ptr %[[MA6]])
+    ++count;
+    break;
+  }
+  while (a || true ? false : true) {
+    // Wider decision comes first.
+    // MM2:  Decision,File 0, [[@LINE-2]]:10 -> [[#L:@LINE-2]]:19 = M:[[#I:I+3]], C:2
+    // MM:   Branch,File 0, [[#L]]:10 -> [[#L]]:11 = ((#0 + #11) - #13), #13 [1,0,2]
+    // MM:   Branch,File 0, [[#L]]:15 -> [[#L]]:19 = (#13 - #14), 0 [2,0,0]
+    // LL:   store i32 0, ptr %[[MA7]], align 4
+    // LL:   = load i32, ptr %[[MA7]], align 4
+    // LL:   store i32 %{{.+}}, ptr %[[MA7]], align 4
+    // LL:   = load i32, ptr %[[MA7]], align 4
+    // LL:   store i32 %{{.+}}, ptr %[[MA7]], align 4
+    // LL:   call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:B+3]], ptr %[[MA7]])
+    ++count;
+  }
+  do {
+    ++count;
+  } while (a && false);
+  // BinOp only
+  // MM:   Decision,File 0, [[@LINE-2]]:12 -> [[#L:@LINE-2]]:22 = M:[[#I:I+3]], C:2
+  // MM:   Branch,File 0, [[#L]]:12 -> [[#L]]:13 = #16, ((#0 + #15) - #16) [1,2,0]
+  // MM:   Branch,File 0, [[#L]]:17 -> [[#L]]:22 = 0, (#16 - #17) [2,0,0]
+  // LL:   store i32 0, ptr %[[MA9]], align 4
+  // LL:   = load i32, ptr %[[MA9]], align 4
+  // LL:   store i32 %{{.+}}, ptr %[[MA9]], align 4
+  // LL:   = load i32, ptr %[[MA9]], align 4
+  // LL:   store i32 %{{.+}}, ptr %[[MA9]], align 4
+  // LL2:  call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:B+3]], ptr %[[MA9]])
+  do {
+    ++count;
+  } while (a && false ? true : false);
+  // Wider decision comes first.
+  // MM2:  Decision,File 0, [[@LINE-2]]:12 -> [[#L:@LINE-2]]:22 = M:15, C:2
+  // MM:   Branch,File 0, [[#L]]:12 -> [[#L]]:13 = #20, ((#0 + #18) - #20) [1,2,0]
+  // MM:   Branch,File 0, [[#L]]:17 -> [[#L]]:22 = 0, (#20 - #21) [2,0,0]
+  // LL:   store i32 0, ptr %[[MA10]], align 4
+  // LL:   = load i32, ptr %[[MA10]], align 4
+  // LL:   store i32 %{{.+}}, ptr %[[MA10]], align 4
+  // LL:   = load i32, ptr %[[MA10]], align 4
+  // LL:   store i32 %{{.+}}, ptr %[[MA10]], align 4
+  // LL:   call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:B+3]], ptr %[[MA10]])
+  // FIXME: Confirm (B+3==BS)
+  for (int i = 0; i < (a ? 2 : 1); ++i) {
+    // Simple nested decision (different column)
+    // MM2-NOT: Decision
+    // LL2-NOT: call void @llvm.instrprof.mcdc.tvbitmap.update
+    ++count;
+  }
+  for (int i = 0; i >= 4 ? false : true; ++i) {
+    // Wider decision comes first.
+    ++count;
+  }
+  return count;
+}
diff --git a/clang/test/Profile/c-mcdc-logicalop-ternary.c b/clang/test/Profile/c-mcdc-logicalop-ternary.c
index 18ce0b4e5dc149..18682ffdbfec38 100644
--- a/clang/test/Profile/c-mcdc-logicalop-ternary.c
+++ b/clang/test/Profile/c-mcdc-logicalop-ternary.c
@@ -13,12 +13,14 @@ int test(int a, int b, int c, int d, int e, int f) {
 
 // ALLOCATE MCDC TEMP AND ZERO IT.
 // MCDC-LABEL: @test(
-// MCDC: %mcdc.addr = alloca i32, align 4
-// MCDC: store i32 0, ptr %mcdc.addr, align 4
+// MCDC: %[[ADDR0:mcdc.+]] = alloca i32, align 4
+// MCDC: %[[ADDR1:mcdc.+]] = alloca i32, align 4
+// MCDC: %[[ADDR2:mcdc.+]] = alloca i32, align 4
+// MCDC: store i32 0, ptr %[[ADDR0]], align 4
 
 // TERNARY TRUE SHOULD UPDATE THE BITMAP WITH RESULT AT ELEMENT 0.
 // MCDC-LABEL: cond.true:
-// MCDC-DAG:  %[[TEMP0:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG:  %[[TEMP0:mcdc.temp[0-9]*]] = load i32, ptr %[[ADDR0]], align 4
 // MCDC:  %[[TEMP:[0-9]+]] = add i32 %[[TEMP0]], 0
 // MCDC:  %[[LAB1:[0-9]+]] = lshr i32 %[[TEMP]], 3
 // MCDC:  %[[LAB4:[0-9]+]] = getelementptr inbounds i8, ptr @__profbm_test, i32 %[[LAB1]]
@@ -30,12 +32,12 @@ int test(int a, int b, int c, int d, int e, int f) {
 // MCDC:  store i8 %[[LAB9]], ptr %[[LAB4]], align 1
 
 // CHECK FOR ZERO OF MCDC TEMP
-// MCDC: store i32 0, ptr %mcdc.addr, align 4
+// MCDC: store i32 0, ptr %[[ADDR1]], align 4
 
 // TERNARY TRUE YIELDS TERNARY LHS LOGICAL-AND.
 // TERNARY LHS LOGICAL-AND SHOULD UPDATE THE BITMAP WITH RESULT AT ELEMENT 1.
 // MCDC-LABEL: land.end:
-// MCDC-DAG:  %[[TEMP0:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG:  %[[TEMP0:mcdc.temp[0-9]*]] = load i32, ptr %[[ADDR1]], align 4
 // MCDC:  %[[TEMP:[0-9]+]] = add i32 %[[TEMP0]], 3
 // MCDC:  %[[LAB1:[0-9]+]] = lshr i32 %[[TEMP]], 3
 // MCDC:  %[[LAB4:[0-9]+]] = getelementptr inbounds i8, ptr @__profbm_test, i32 %[[LAB1]]
@@ -48,7 +50,7 @@ int test(int a, int b, int c, int d, int e, int f) {
 
 // TERNARY FALSE SHOULD UPDATE THE BITMAP WITH RESULT AT ELEMENT 0.
 // MCDC-LABEL: cond.false:
-// MCDC-DAG:  %[[TEMP0:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG: %[[TEMP0:mcdc.+]] = load i32, ptr %[[ADDR0]], align 4
 // MCDC:  %[[TEMP:[0-9]+]] = add i32 %[[TEMP0]], 0
 // MCDC:  %[[LAB1:[0-9]+]] = lshr i32 %[[TEMP]], 3
 // MCDC:  %[[LAB4:[0-9]+]] = getelementptr inbounds i8, ptr @__profbm_test, i32 %[[LAB1]]
@@ -60,12 +62,12 @@ int test(int a, int b, int c, int d, int e, int f) {
 // MCDC:  store i8 %[[LAB9]], ptr %[[LAB4]], align 1
 
 // CHECK FOR ZERO OF MCDC TEMP
-// MCDC: store i32 0, ptr %mcdc.addr, align 4
+// MCDC: store i32 0, ptr %[[ADDR2]], align 4
 
 // TERNARY FALSE YIELDS TERNARY RHS LOGICAL-OR.
 // TERNARY RHS LOGICAL-OR SHOULD UPDATE THE BITMAP WITH RESULT AT ELEMENT 2.
 // MCDC-LABEL: lor.end:
-// MCDC-DAG:  %[[TEMP0:mcdc.temp[0-9]*]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG:  %[[TEMP0:mcdc.temp[0-9]*]] = load i32, ptr %[[ADDR2]], align 4
 // MCDC:  %[[TEMP:[0-9]+]] = add i32 %[[TEMP0]], 6
 // MCDC:  %[[LAB1:[0-9]+]] = lshr i32 %[[TEMP]], 3
 // MCDC:  %[[LAB4:[0-9]+]] = getelementptr inbounds i8, ptr @__profbm_test, i32 %[[LAB1]]

``````````

</details>


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


More information about the llvm-branch-commits mailing list