[llvm-branch-commits] [clang] [MC/DC] Create dedicated MCDCCondBitmapAddr for each Decision (PR #125411)
NAKAMURA Takumi via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun Feb 2 05:34:40 PST 2025
https://github.com/chapuni created https://github.com/llvm/llvm-project/pull/125411
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.
>From c74e5af3fb458230931d7cbba5d32e5999c38bf4 Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Sun, 2 Feb 2025 22:01:40 +0900
Subject: [PATCH] [MC/DC] Create dedicated MCDCCondBitmapAddr for each Decision
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.
---
clang/lib/CodeGen/CodeGenFunction.h | 17 +--
clang/lib/CodeGen/CodeGenPGO.cpp | 41 +++++--
clang/lib/CodeGen/CodeGenPGO.h | 8 +-
clang/lib/CodeGen/MCDCState.h | 2 +
.../test/CoverageMapping/mcdc-single-cond.cpp | 101 ++++++++++++++++++
clang/test/Profile/c-mcdc-logicalop-ternary.c | 18 ++--
6 files changed, 160 insertions(+), 27 deletions(-)
create mode 100644 clang/test/CoverageMapping/mcdc-single-cond.cpp
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]]
More information about the llvm-branch-commits
mailing list