[clang] [MC/DC] Enable usage of `!` among `&&` and `||` (PR #125406)

NAKAMURA Takumi via cfe-commits cfe-commits at lists.llvm.org
Fri Feb 28 23:06:06 PST 2025


https://github.com/chapuni updated https://github.com/llvm/llvm-project/pull/125406

>From a6d4be0e4b05b411c7160385485cfed0f173965c Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Sun, 2 Feb 2025 21:55:43 +0900
Subject: [PATCH 1/4] [MC/DC] Update CoverageMapping tests

To resolve the error, rename mcdc-error-nests.cpp -> mcdc-nested-expr.cpp at first.

- `func_condop`
  A corner case that contains close decisions.
- `func_expect`
  Uses `__builtin_expect`. (#124565)
- `func_lnot`
  Contains logical not(s) `!` among MC/DC binary operators. (#124563)

mcdc-single-cond.cpp is for #95336.
---
 .../test/CoverageMapping/mcdc-error-nests.cpp | 10 --
 .../test/CoverageMapping/mcdc-nested-expr.cpp | 34 +++++++
 .../test/CoverageMapping/mcdc-single-cond.cpp | 97 +++++++++++++++++++
 3 files changed, 131 insertions(+), 10 deletions(-)
 delete mode 100644 clang/test/CoverageMapping/mcdc-error-nests.cpp
 create mode 100644 clang/test/CoverageMapping/mcdc-nested-expr.cpp
 create mode 100644 clang/test/CoverageMapping/mcdc-single-cond.cpp

diff --git a/clang/test/CoverageMapping/mcdc-error-nests.cpp b/clang/test/CoverageMapping/mcdc-error-nests.cpp
deleted file mode 100644
index 3add2b9ccd3fb..0000000000000
--- a/clang/test/CoverageMapping/mcdc-error-nests.cpp
+++ /dev/null
@@ -1,10 +0,0 @@
-// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 -fcoverage-mcdc -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only %s 2>&1| FileCheck %s
-
-// "Split-nest" -- boolean expressions within boolean expressions.
-extern bool bar(bool);
-bool func_split_nest(bool a, bool b, bool c, bool d, bool e, bool f, bool g) {
-  bool res = a && b && c && bar(d && e) && f && g;
-  return bar(res);
-}
-
-// CHECK: warning: unsupported MC/DC boolean expression; contains an operation with a nested boolean expression.
diff --git a/clang/test/CoverageMapping/mcdc-nested-expr.cpp b/clang/test/CoverageMapping/mcdc-nested-expr.cpp
new file mode 100644
index 0000000000000..bb82873e3b600
--- /dev/null
+++ b/clang/test/CoverageMapping/mcdc-nested-expr.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 -fcoverage-mcdc -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only %s 2> %t.stderr.txt | FileCheck %s
+// RUN: FileCheck %s --check-prefix=WARN < %t.stderr.txt
+
+// "Split-nest" -- boolean expressions within boolean expressions.
+extern bool bar(bool);
+// CHECK: func_split_nest{{.*}}:
+bool func_split_nest(bool a, bool b, bool c, bool d, bool e, bool f, bool g) {
+  // WARN: :[[@LINE+1]]:14: warning: unsupported MC/DC boolean expression; contains an operation with a nested boolean expression.
+  bool res = a && b && c && bar(d && e) && f && g;
+  return bar(res);
+}
+
+// The inner expr begins with the same Loc as the outer expr
+// CHECK: func_condop{{.*}}:
+bool func_condop(bool a, bool b, bool c) {
+  // WARN: :[[@LINE+1]]:10: warning: unsupported MC/DC boolean expression; contains an operation with a nested boolean expression.
+  return (a && b ? true : false) && c;
+}
+
+// __builtin_expect
+// Treated as parentheses.
+// CHECK: func_expect{{.*}}:
+bool func_expect(bool a, bool b, bool c) {
+  // WARN: :[[@LINE+1]]:10: warning: unsupported MC/DC boolean expression; contains an operation with a nested boolean expression.
+  return a || __builtin_expect(b && c, true);
+}
+
+// LNot among BinOp(s)
+// Doesn't split exprs.
+// CHECK: func_lnot{{.*}}:
+bool func_lnot(bool a, bool b, bool c, bool d) {
+  // WARN: :[[@LINE+1]]:10: warning: unsupported MC/DC boolean expression; contains an operation with a nested boolean expression.
+  return !(a || b) && !(c && d);
+}
diff --git a/clang/test/CoverageMapping/mcdc-single-cond.cpp b/clang/test/CoverageMapping/mcdc-single-cond.cpp
new file mode 100644
index 0000000000000..b1eeea879e521
--- /dev/null
+++ b/clang/test/CoverageMapping/mcdc-single-cond.cpp
@@ -0,0 +1,97 @@
+// 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:   %[[MA: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 %[[MA]], align 4
+    // LL:   = load i32, ptr %[[MA]], align 4
+    // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
+    // LL:   = load i32, ptr %[[MA]], align 4
+    // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
+    // LL2:  call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:0]], ptr %[[MA]])
+    ++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 %[[MA]], align 4
+    // LL:   = load i32, ptr %[[MA]], align 4
+    // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
+    // LL:   = load i32, ptr %[[MA]], align 4
+    // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
+    // LL2:  call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:B+3]], ptr %[[MA]])
+    ++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 %[[MA]], align 4
+    // LL:   = load i32, ptr %[[MA]], align 4
+    // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
+    // LL:   = load i32, ptr %[[MA]], align 4
+    // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
+    // LL:   call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:B+3]], ptr %[[MA]])
+    ++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 %[[MA]], align 4
+  // LL:   = load i32, ptr %[[MA]], align 4
+  // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
+  // LL:   = load i32, ptr %[[MA]], align 4
+  // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
+  // LL2:  call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:B+3]], ptr %[[MA]])
+  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 %[[MA]], align 4
+  // LL:   = load i32, ptr %[[MA]], align 4
+  // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
+  // LL:   = load i32, ptr %[[MA]], align 4
+  // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
+  // LL:   call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:B+3]], ptr %[[MA]])
+  // 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;
+}

>From f2cf50e10b59d7d461967baef4d589c9282d0f6d Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Sun, 2 Feb 2025 22:11:51 +0900
Subject: [PATCH 2/4] [MC/DC] Enable usage of `!` among `&&` and `||`

In the current implementation, `!(a || b) && c` was not treated as one
Decision with three terms.

Fixes #124563
---
 clang/include/clang/AST/IgnoreExpr.h          |  8 ++
 clang/lib/CodeGen/CodeGenFunction.cpp         | 12 ++-
 clang/lib/CodeGen/CodeGenPGO.cpp              |  8 +-
 clang/lib/CodeGen/CoverageMappingGen.cpp      | 12 +++
 .../test/CoverageMapping/mcdc-nested-expr.cpp |  6 +-
 clang/test/Profile/c-mcdc-not.c               | 95 +++++++++++++++++++
 6 files changed, 132 insertions(+), 9 deletions(-)

diff --git a/clang/include/clang/AST/IgnoreExpr.h b/clang/include/clang/AST/IgnoreExpr.h
index 917bada61fa6f..c48c0c0daf815 100644
--- a/clang/include/clang/AST/IgnoreExpr.h
+++ b/clang/include/clang/AST/IgnoreExpr.h
@@ -134,6 +134,14 @@ inline Expr *IgnoreElidableImplicitConstructorSingleStep(Expr *E) {
   return E;
 }
 
+inline Expr *IgnoreUOpLNotSingleStep(Expr *E) {
+  if (auto *UO = dyn_cast<UnaryOperator>(E)) {
+    if (UO->getOpcode() == UO_LNot)
+      return UO->getSubExpr();
+  }
+  return E;
+}
+
 inline Expr *IgnoreImplicitAsWrittenSingleStep(Expr *E) {
   if (auto *ICE = dyn_cast<ImplicitCastExpr>(E))
     return ICE->getSubExprAsWritten();
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index bbef277a52448..2c380ac926b1e 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -27,6 +27,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/Expr.h"
+#include "clang/AST/IgnoreExpr.h"
 #include "clang/AST/StmtCXX.h"
 #include "clang/AST/StmtObjC.h"
 #include "clang/Basic/Builtins.h"
@@ -1748,12 +1749,13 @@ bool CodeGenFunction::ConstantFoldsToSimpleInteger(const Expr *Cond,
 
 /// Strip parentheses and simplistic logical-NOT operators.
 const Expr *CodeGenFunction::stripCond(const Expr *C) {
-  while (const UnaryOperator *Op = dyn_cast<UnaryOperator>(C->IgnoreParens())) {
-    if (Op->getOpcode() != UO_LNot)
-      break;
-    C = Op->getSubExpr();
+  while (true) {
+    const Expr *SC =
+        IgnoreExprNodes(C, IgnoreParensSingleStep, IgnoreUOpLNotSingleStep);
+    if (C == SC)
+      return SC;
+    C = SC;
   }
-  return C->IgnoreParens();
 }
 
 /// Determine whether the given condition is an instrumentable condition
diff --git a/clang/lib/CodeGen/CodeGenPGO.cpp b/clang/lib/CodeGen/CodeGenPGO.cpp
index 792373839107f..0fd49b880bba3 100644
--- a/clang/lib/CodeGen/CodeGenPGO.cpp
+++ b/clang/lib/CodeGen/CodeGenPGO.cpp
@@ -247,8 +247,9 @@ struct MapRegionCounters : public RecursiveASTVisitor<MapRegionCounters> {
     }
 
     if (const Expr *E = dyn_cast<Expr>(S)) {
-      const BinaryOperator *BinOp = dyn_cast<BinaryOperator>(E->IgnoreParens());
-      if (BinOp && BinOp->isLogicalOp()) {
+      if (const auto *BinOp =
+              dyn_cast<BinaryOperator>(CodeGenFunction::stripCond(E));
+          BinOp && BinOp->isLogicalOp()) {
         /// Check for "split-nested" logical operators. This happens when a new
         /// boolean expression logical-op nest is encountered within an existing
         /// boolean expression, separated by a non-logical operator.  For
@@ -280,7 +281,8 @@ struct MapRegionCounters : public RecursiveASTVisitor<MapRegionCounters> {
       return true;
 
     if (const Expr *E = dyn_cast<Expr>(S)) {
-      const BinaryOperator *BinOp = dyn_cast<BinaryOperator>(E->IgnoreParens());
+      const BinaryOperator *BinOp =
+          dyn_cast<BinaryOperator>(CodeGenFunction::stripCond(E));
       if (BinOp && BinOp->isLogicalOp()) {
         assert(LogOpStack.back() == BinOp);
         LogOpStack.pop_back();
diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp
index f09157771d2b5..9bf73cf27a5fa 100644
--- a/clang/lib/CodeGen/CoverageMappingGen.cpp
+++ b/clang/lib/CodeGen/CoverageMappingGen.cpp
@@ -799,6 +799,12 @@ struct MCDCCoverageBuilder {
   /// Return the LHS Decision ([0,0] if not set).
   const mcdc::ConditionIDs &back() const { return DecisionStack.back(); }
 
+  void swapConds() {
+    if (DecisionStack.empty())
+      return;
+
+    std::swap(DecisionStack.back()[false], DecisionStack.back()[true]);
+  }
   /// Push the binary operator statement to track the nest level and assign IDs
   /// to the operator's LHS and RHS.  The RHS may be a larger subtree that is
   /// broken up on successive levels.
@@ -2241,6 +2247,12 @@ struct CounterCoverageMappingBuilder
             SM.isInSystemHeader(SM.getSpellingLoc(E->getEndLoc())));
   }
 
+  void VisitUnaryLNot(const UnaryOperator *E) {
+    MCDCBuilder.swapConds();
+    Visit(E->getSubExpr());
+    MCDCBuilder.swapConds();
+  }
+
   void VisitBinLAnd(const BinaryOperator *E) {
     if (isExprInSystemHeader(E)) {
       LeafExprSet.insert(E);
diff --git a/clang/test/CoverageMapping/mcdc-nested-expr.cpp b/clang/test/CoverageMapping/mcdc-nested-expr.cpp
index bb82873e3b600..a49dad0b33612 100644
--- a/clang/test/CoverageMapping/mcdc-nested-expr.cpp
+++ b/clang/test/CoverageMapping/mcdc-nested-expr.cpp
@@ -29,6 +29,10 @@ bool func_expect(bool a, bool b, bool c) {
 // Doesn't split exprs.
 // CHECK: func_lnot{{.*}}:
 bool func_lnot(bool a, bool b, bool c, bool d) {
-  // WARN: :[[@LINE+1]]:10: warning: unsupported MC/DC boolean expression; contains an operation with a nested boolean expression.
   return !(a || b) && !(c && d);
+  // CHECK:  Decision,File 0, [[@LINE-1]]:10 -> [[#L:@LINE-1]]:32 = M:5, C:4
+  // CHECK:  Branch,File 0, [[#L]]:12 -> [[#L]]:13 = (#0 - #2), #2 [1,0,3]
+  // CHECK:  Branch,File 0, [[#L]]:17 -> [[#L]]:18 = (#2 - #3), #3 [3,0,2]
+  // CHECK:  Branch,File 0, [[#L]]:25 -> [[#L]]:26 = #4, (#1 - #4) [2,4,0]
+  // CHECK:  Branch,File 0, [[#L]]:30 -> [[#L]]:31 = #5, (#4 - #5) [4,0,0]
 }
diff --git a/clang/test/Profile/c-mcdc-not.c b/clang/test/Profile/c-mcdc-not.c
index 7083aa226fb95..d6d79bf15cb08 100644
--- a/clang/test/Profile/c-mcdc-not.c
+++ b/clang/test/Profile/c-mcdc-not.c
@@ -85,3 +85,98 @@ int test(int a, int b, int c, int d, int e, int f) {
 // MCDC:  %[[BITS:.+]] = load i8, ptr %[[LAB4]], align 1
 // MCDC:  %[[LAB8:[0-9]+]] = or i8 %[[BITS]], %[[LAB7]]
 // MCDC:  store i8 %[[LAB8]], ptr %[[LAB4]], align 1
+
+int internot(int a, int b, int c, int d, int e, int f) {
+  return !(!(!a && b) || !(!!(!c && d) || !(e && !f)));
+}
+
+// MCDC-LABEL: @internot(
+// MCDC-DAG:  store i32 0, ptr %mcdc.addr, align 4
+
+// Branch #2, (#0 - #2) [1,3,0]
+// !a [+0 => b][+6 => END]
+// MCDC-DAG:  %[[A:.+]] = load i32, ptr %a.addr, align 4
+// MCDC-DAG:  %[[AB:.+]] = icmp ne i32 %[[A]], 0
+// MCDC-DAG:  %[[AN:.+]] = xor i1 %[[AB]], true
+// MCDC-DAG:  %[[M1:.+]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG:  %[[M1T:.+]] = add i32 %[[M1]], 0
+// MCDC-DAG:  %[[M1F:.+]] = add i32 %[[M1]], 6
+// MCDC-DAG:  %[[M1S:.+]] = select i1 %[[AN]], i32 %[[M1T]], i32 %[[M1F]]
+// MCDC-DAG:  store i32 %[[M1S]], ptr %mcdc.addr, align 4
+
+// Branch #3, (#2 - #3) [3,2,0]
+// b [+0 => c][+7 => END]
+// MCDC-DAG:  %[[B:.+]] = load i32, ptr %b.addr, align 4
+// MCDC-DAG:  %[[BB:.+]] = icmp ne i32 %[[B]], 0
+// MCDC-DAG:  %[[M3:.+]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG:  %[[M3T:.+]] = add i32 %[[M3]], 0
+// MCDC-DAG:  %[[M3F:.+]] = add i32 %[[M3]], 7
+// MCDC-DAG:  %[[M3S:.+]] = select i1 %[[BB]], i32 %[[M3T]], i32 %[[M3F]]
+// MCDC-DAG:  store i32 %[[M3S]], ptr %mcdc.addr, align 4
+
+// Branch #5, (#1 - #5) [2,5,4]
+// !c [+0 => d][+0 => e]
+// MCDC-DAG:  %[[C:.+]] = load i32, ptr %c.addr, align 4
+// MCDC-DAG:  %[[CB:.+]] = icmp ne i32 %[[C]], 0
+// MCDC-DAG:  %[[CN:.+]] = xor i1 %[[CB]], true
+// MCDC-DAG:  %[[M2:.+]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG:  %[[M2T:.+]] = add i32 %[[M2]], 0
+// MCDC-DAG:  %[[M2F:.+]] = add i32 %[[M2]], 0
+// MCDC-DAG:  %[[M2S:.+]] = select i1 %[[CN]], i32 %[[M2T]], i32 %[[M2F]]
+// MCDC-DAG:  store i32 %[[M2S]], ptr %mcdc.addr, align 4
+
+// Branch #6, (#5 - #6) [5,0,4]
+// d [+8 => END][+1 => e]]
+// MCDC-DAG:  %[[D:.+]] = load i32, ptr %d.addr, align 4
+// MCDC-DAG:  %[[DB:.+]] = icmp ne i32 %[[D]], 0
+// MCDC-DAG:  %[[M5:.+]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG:  %[[M5T:.+]] = add i32 %[[M5]], 8
+// MCDC-DAG:  %[[M5F:.+]] = add i32 %[[M5]], 1
+// MCDC-DAG:  %[[M5S:.+]] = select i1 %[[DB]], i32 %[[M5T]], i32 %[[M5F]]
+// MCDC-DAG:  store i32 %[[M5S]], ptr %mcdc.addr, align 4
+
+// Branch #7, (#4 - #7) [4,6,0]
+// e [+0 => f][+0 => END]
+// from:
+//   [c => +0]
+//   [d => +1]
+// MCDC-DAG:  %[[E:.+]] = load i32, ptr %e.addr, align 4
+// MCDC-DAG:  %[[EB:.+]] = icmp ne i32 %[[E]], 0
+// MCDC-DAG:  %[[M4:.+]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG:  %[[M4T:.+]] = add i32 %[[M4]], 0
+// MCDC-DAG:  %[[M4F:.+]] = add i32 %[[M4]], 0
+// MCDC-DAG:  %[[M4S:.+]] = select i1 %[[EB]], i32 %[[M4T]], i32 %[[M4F]]
+// MCDC-DAG:  store i32 %[[M4S]], ptr %mcdc.addr, align 4
+
+// Branch #8, (#7 - #8) [6,0,0]
+// !f [+4 => END][+2 => END]
+// MCDC-DAG:  %[[F:.+]] = load i32, ptr %f.addr, align 4
+// MCDC-DAG:  %[[FB:.+]] = icmp ne i32 %[[F]], 0
+// MCDC-DAG:  %[[FN:.+]] = xor i1 %[[FB]], true
+// MCDC-DAG:  %[[M6:.+]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG:  %[[M6T:.+]] = add i32 %[[M6]], 4
+// MCDC-DAG:  %[[M6F:.+]] = add i32 %[[M6]], 2
+// MCDC-DAG:  %[[M6S:.+]] = select i1 %[[FN]], i32 %[[M6T]], i32 %[[M6F]]
+// MCDC-DAG:  store i32 %[[M6S]], ptr %mcdc.addr, align 4
+
+// from:
+//   [e => +0]
+//   [f => +2]
+//     [c => +0]
+//     [d => +1]
+//   [f => +4]
+//     [c => +0]
+//     [d => +1]
+//   [a => +6]
+//   [b => +7]
+//   [d => +8]
+// MCDC-DAG:  %[[T0:.+]] = load i32, ptr %mcdc.addr, align 4
+// MCDC-DAG:  %[[T:.+]] = add i32 %[[T0]], 0
+// MCDC-DAG:  %[[TA:.+]] = lshr i32 %[[T]], 3
+// MCDC-DAG:  %[[BA:.+]] = getelementptr inbounds i8, ptr @__profbm_internot, i32 %[[TA]]
+// MCDC-DAG:  %[[BI:.+]] = and i32 %[[T]], 7
+// MCDC-DAG:  %[[BI1:.+]] = trunc i32 %[[BI]] to i8
+// MCDC-DAG:  %[[BM:.+]] = shl i8 1, %[[BI1]]
+// MCDC-DAG:  %mcdc.bits = load i8, ptr %[[BA]], align 1
+// MCDC-DAG:  %[[BN:.+]] = or i8 %mcdc.bits, %[[BM]]
+// MCDC-DAG:  store i8 %[[BN]], ptr %[[BA]], align 1

>From 7c71ae3369b1df23c8a3e65a2950885b6162e06b Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Mon, 3 Feb 2025 20:30:27 +0900
Subject: [PATCH 3/4] Update ReleaseNotes

---
 clang/docs/ReleaseNotes.rst | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index b8b47103d9517..22f52b388fd40 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -116,6 +116,9 @@ Improvements to Clang's time-trace
 Improvements to Coverage Mapping
 --------------------------------
 
+- [MC/DC] Unary logical not `!` among binary operators is recognized
+  as a part of the expression. (#GH124563)
+
 Bug Fixes in This Version
 -------------------------
 

>From 8b35e76018417c301dc757ea5446292dd3a3996c Mon Sep 17 00:00:00 2001
From: NAKAMURA Takumi <geek4civic at gmail.com>
Date: Sat, 1 Mar 2025 15:52:53 +0900
Subject: [PATCH 4/4] Remove mcdc-single-cond.cpp

---
 .../test/CoverageMapping/mcdc-single-cond.cpp | 97 -------------------
 1 file changed, 97 deletions(-)
 delete mode 100644 clang/test/CoverageMapping/mcdc-single-cond.cpp

diff --git a/clang/test/CoverageMapping/mcdc-single-cond.cpp b/clang/test/CoverageMapping/mcdc-single-cond.cpp
deleted file mode 100644
index b1eeea879e521..0000000000000
--- a/clang/test/CoverageMapping/mcdc-single-cond.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-// 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:   %[[MA: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 %[[MA]], align 4
-    // LL:   = load i32, ptr %[[MA]], align 4
-    // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
-    // LL:   = load i32, ptr %[[MA]], align 4
-    // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
-    // LL2:  call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:0]], ptr %[[MA]])
-    ++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 %[[MA]], align 4
-    // LL:   = load i32, ptr %[[MA]], align 4
-    // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
-    // LL:   = load i32, ptr %[[MA]], align 4
-    // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
-    // LL2:  call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:B+3]], ptr %[[MA]])
-    ++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 %[[MA]], align 4
-    // LL:   = load i32, ptr %[[MA]], align 4
-    // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
-    // LL:   = load i32, ptr %[[MA]], align 4
-    // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
-    // LL:   call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:B+3]], ptr %[[MA]])
-    ++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 %[[MA]], align 4
-  // LL:   = load i32, ptr %[[MA]], align 4
-  // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
-  // LL:   = load i32, ptr %[[MA]], align 4
-  // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
-  // LL2:  call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:B+3]], ptr %[[MA]])
-  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 %[[MA]], align 4
-  // LL:   = load i32, ptr %[[MA]], align 4
-  // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
-  // LL:   = load i32, ptr %[[MA]], align 4
-  // LL:   store i32 %{{.+}}, ptr %[[MA]], align 4
-  // LL:   call void @llvm.instrprof.mcdc.tvbitmap.update(ptr @[[PROFN]], i64 [[#H]], i32 [[#B:B+3]], ptr %[[MA]])
-  // 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;
-}



More information about the cfe-commits mailing list