Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Coverage] Handles macros from system headers and nested macros in scratch space #94438

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 82 additions & 11 deletions clang/lib/CodeGen/CoverageMappingGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "clang/Basic/FileManager.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Lex/Lexer.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ProfileData/Coverage/CoverageMapping.h"
Expand Down Expand Up @@ -278,10 +279,36 @@ class CoverageMappingBuilder {
return SM.getLocForEndOfFile(SM.getFileID(Loc));
}

/// Find out where the current file is included or macro is expanded.
SourceLocation getIncludeOrExpansionLoc(SourceLocation Loc) {
return Loc.isMacroID() ? SM.getImmediateExpansionRange(Loc).getBegin()
: SM.getIncludeLoc(SM.getFileID(Loc));
/// Find out where a macro is expanded. If the immediate result is a
/// <scratch space>, keep looking until the result isn't. Return a pair of
/// \c SourceLocation. The first object is always the begin sloc of found
/// result. The second should be checked by the caller: if it has value, it's
/// the end sloc of the found result. Otherwise the while loop didn't get
/// executed, which means the location wasn't changed and the caller has to
/// learn the end sloc from somewhere else.
std::pair<SourceLocation, std::optional<SourceLocation>>
getNonScratchExpansionLoc(SourceLocation Loc) {
std::optional<SourceLocation> EndLoc = std::nullopt;
while (Loc.isMacroID() &&
SM.isWrittenInScratchSpace(SM.getSpellingLoc(Loc))) {
auto ExpansionRange = SM.getImmediateExpansionRange(Loc);
Loc = ExpansionRange.getBegin();
EndLoc = ExpansionRange.getEnd();
}
return std::make_pair(Loc, EndLoc);
}

/// Find out where the current file is included or macro is expanded. If
/// \c AcceptScratch is set to false, keep looking for expansions until the
/// found sloc is not a <scratch space>.
SourceLocation getIncludeOrExpansionLoc(SourceLocation Loc,
bool AcceptScratch = true) {
if (!Loc.isMacroID())
return SM.getIncludeLoc(SM.getFileID(Loc));
Loc = SM.getImmediateExpansionRange(Loc).getBegin();
if (AcceptScratch)
return Loc;
return getNonScratchExpansionLoc(Loc).first;
}

/// Return true if \c Loc is a location in a built-in macro.
Expand Down Expand Up @@ -325,16 +352,35 @@ class CoverageMappingBuilder {

llvm::SmallSet<FileID, 8> Visited;
SmallVector<std::pair<SourceLocation, unsigned>, 8> FileLocs;
for (const auto &Region : SourceRegions) {
for (auto &Region : SourceRegions) {
SourceLocation Loc = Region.getBeginLoc();

// Replace Region with its definition if it is in <scratch space>.
auto NonScratchExpansionLoc = getNonScratchExpansionLoc(Loc);
auto EndLoc = NonScratchExpansionLoc.second;
if (EndLoc.has_value()) {
Loc = NonScratchExpansionLoc.first;
Region.setStartLoc(Loc);
Region.setEndLoc(EndLoc.value());
}

// Replace Loc with FileLoc if it is expanded with system headers.
if (!SystemHeadersCoverage && SM.isInSystemMacro(Loc)) {
auto BeginLoc = SM.getSpellingLoc(Loc);
auto EndLoc = SM.getSpellingLoc(Region.getEndLoc());
if (SM.isWrittenInSameFile(BeginLoc, EndLoc)) {
Loc = SM.getFileLoc(Loc);
Region.setStartLoc(Loc);
Region.setEndLoc(SM.getFileLoc(Region.getEndLoc()));
}
}

FileID File = SM.getFileID(Loc);
if (!Visited.insert(File).second)
continue;

// Do not map FileID's associated with system headers unless collecting
// coverage from system headers is explicitly enabled.
if (!SystemHeadersCoverage && SM.isInSystemHeader(SM.getSpellingLoc(Loc)))
continue;
assert(SystemHeadersCoverage ||
!SM.isInSystemHeader(SM.getSpellingLoc(Loc)));

unsigned Depth = 0;
for (SourceLocation Parent = getIncludeOrExpansionLoc(Loc);
Expand Down Expand Up @@ -503,7 +549,7 @@ class CoverageMappingBuilder {
SourceRegionFilter Filter;
for (const auto &FM : FileIDMapping) {
SourceLocation ExpandedLoc = FM.second.second;
SourceLocation ParentLoc = getIncludeOrExpansionLoc(ExpandedLoc);
SourceLocation ParentLoc = getIncludeOrExpansionLoc(ExpandedLoc, false);
if (ParentLoc.isInvalid())
continue;

Expand Down Expand Up @@ -816,6 +862,10 @@ struct CounterCoverageMappingBuilder
/// A stack of currently live regions.
llvm::SmallVector<SourceMappingRegion> RegionStack;

/// Set if the Expr should be handled as a leaf even if it is kind of binary
/// logical ops (&&, ||).
llvm::DenseSet<const Stmt *> LeafExprSet;

/// An object to manage MCDC regions.
MCDCCoverageBuilder MCDCBuilder;

Expand Down Expand Up @@ -1041,7 +1091,10 @@ struct CounterCoverageMappingBuilder
// region onto RegionStack but immediately pop it (which adds it to the
// function's SourceRegions) because it doesn't apply to any other source
// code other than the Condition.
if (CodeGenFunction::isInstrumentedCondition(C)) {
// With !SystemHeadersCoverage, binary logical ops in system headers may be
// treated as instrumentable conditions.
if (CodeGenFunction::isInstrumentedCondition(C) ||
LeafExprSet.count(CodeGenFunction::stripCond(C))) {
MCDCConditionID ID = MCDCBuilder.getCondID(C);
MCDCConditionID TrueID = IDPair.TrueID;
MCDCConditionID FalseID = IDPair.FalseID;
Expand Down Expand Up @@ -1977,7 +2030,20 @@ struct CounterCoverageMappingBuilder
subtractCounters(ParentCount, TrueCount));
}

/// Check if E belongs to system headers.
bool isExprInSystemHeader(const BinaryOperator *E) const {
return (!SystemHeadersCoverage &&
SM.isInSystemHeader(SM.getSpellingLoc(E->getOperatorLoc())) &&
SM.isInSystemHeader(SM.getSpellingLoc(E->getBeginLoc())) &&
SM.isInSystemHeader(SM.getSpellingLoc(E->getEndLoc())));
}

void VisitBinLAnd(const BinaryOperator *E) {
if (isExprInSystemHeader(E)) {
LeafExprSet.insert(E);
return;
}

bool IsRootNode = MCDCBuilder.isIdle();

// Keep track of Binary Operator and assign MCDC condition IDs.
Expand Down Expand Up @@ -2031,6 +2097,11 @@ struct CounterCoverageMappingBuilder
}

void VisitBinLOr(const BinaryOperator *E) {
if (isExprInSystemHeader(E)) {
LeafExprSet.insert(E);
return;
}

bool IsRootNode = MCDCBuilder.isIdle();

// Keep track of Binary Operator and assign MCDC condition IDs.
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CoverageMapping/builtinmacro.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

// CHECK: filename
const char *filename (const char *name) { // CHECK-NEXT: File 0, [[@LINE]]:41 -> [[@LINE+3]]:2 = #0
static const char this_file[] = __FILE__;
static const char this_file[] = __FILE__; // CHECK-NEXT: File 0, [[@LINE]]:35 -> [[@LINE]]:35 = #0
return this_file;
}

Expand Down
8 changes: 5 additions & 3 deletions clang/test/CoverageMapping/macros.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,14 @@ void func7(void) { // CHECK-NEXT: File 0, [[@LINE]]:18 -> [[@LINE+6]]:2 = #0
int kk,ll; // CHECK-NEXT: File 0, [[@LINE+1]]:7 -> [[@LINE+1]]:8 = #0
if (k) // CHECK-NEXT: Branch,File 0, [[@LINE]]:7 -> [[@LINE]]:8 = #1
m(k); // CHECK-NEXT: Gap,File 0, [[@LINE-1]]:9 -> [[@LINE]]:5 = #1
else // CHECK-NEXT: Expansion,File 0, [[@LINE-1]]:5 -> [[@LINE-1]]:6 = #0
else // CHECK-NEXT: Expansion,File 0, [[@LINE-1]]:5 -> [[@LINE-1]]:6 = #1
l = m(l); // CHECK-NEXT: Gap,File 0, [[@LINE-2]]:7 -> [[@LINE]]:5 = (#0 - #1)
} // CHECK-NEXT: File 0, [[@LINE-1]]:5 -> [[@LINE-1]]:10 = (#0 - #1)
// CHECK-NEXT: Expansion,File 0, [[@LINE-2]]:9 -> [[@LINE-2]]:10 = (#0 - #1)
// CHECK-NEXT: File 1, [[@LINE-9]]:14 -> [[@LINE-9]]:18 = #0
// CHECK-NEXT: File 2, [[@LINE-10]]:14 -> [[@LINE-10]]:15 = (#0 - #1)
// CHECK-NEXT: File 1, [[@LINE-9]]:14 -> [[@LINE-9]]:17 = #1
// CHECK-NEXT: File 1, [[@LINE-10]]:14 -> [[@LINE-10]]:18 = #0
// CHECK-NEXT: File 2, [[@LINE-11]]:14 -> [[@LINE-11]]:17 = (#0 - #1)
// CHECK-NEXT: File 2, [[@LINE-12]]:14 -> [[@LINE-12]]:15 = (#0 - #1)

int main(int argc, const char *argv[]) {
func();
Expand Down
65 changes: 65 additions & 0 deletions clang/test/CoverageMapping/mcdc-scratch-space.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c99 -fcoverage-mcdc -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only %s | FileCheck %s

// CHECK: builtin_macro0:
int builtin_macro0(int a) {
// CHECK: Decision,File 0, [[@LINE+1]]:11 -> [[@LINE+2]]:15 = M:0, C:2
return (__LINE__ // CHECK: Branch,File 0, [[@LINE]]:11 -> [[@LINE]]:11 = 0, 0 [1,2,0]
&& a); // CHECK: Branch,File 0, [[@LINE]]:14 -> [[@LINE]]:15 = #2, (#1 - #2) [2,0,0]
}

// CHECK: builtin_macro1:
int builtin_macro1(int a) {
// CHECK: Decision,File 0, [[@LINE+1]]:11 -> [[@LINE+2]]:22 = M:0, C:2
return (a // CHECK: Branch,File 0, [[@LINE]]:11 -> [[@LINE]]:12 = (#0 - #1), #1 [1,0,2]
|| __LINE__); // CHECK: Branch,File 0, [[@LINE]]:14 -> [[@LINE]]:14 = 0, 0 [2,0,0]
}

#define PRE(x) pre_##x

// CHECK: pre0:
int pre0(int pre_a, int b_post) {
// CHECK: Decision,File 0, [[@LINE+2]]:11 -> [[@LINE+3]]:20 = M:0, C:2
// CHECK: Expansion,File 0, [[@LINE+1]]:11 -> [[@LINE+1]]:14 = #0 (Expanded file = 1)
return (PRE(a)
&& b_post);
// CHECK: Branch,File 0, [[@LINE-1]]:14 -> [[@LINE-1]]:20 = #2, (#1 - #2) [2,0,0]
// CHECK: Branch,File 1, [[@LINE-9]]:16 -> [[@LINE-9]]:22 = #1, (#0 - #1) [1,2,0]
}

#define pre_foo pre_a

// CHECK: pre1:
int pre1(int pre_a, int b_post) {
// CHECK: Decision,File 0, [[@LINE+3]]:11 -> [[@LINE+4]]:20 = M:0, C:2
// CHECK: Expansion,File 0, [[@LINE+2]]:11 -> [[@LINE+2]]:14 = #0 (Expanded file = 1)
// CHECK: Branch,File 0, [[@LINE+2]]:14 -> [[@LINE+2]]:20 = #2, (#1 - #2) [2,0,0]
return (PRE(foo)
&& b_post);
// CHECK: Expansion,File 1, 17:16 -> 17:20 = #0 (Expanded file = 2)
// CHECK: Branch,File 2, 29:17 -> 29:22 = #1, (#0 - #1) [1,2,0]
}

#define POST(x) x##_post

// CHECK: post0:
int post0(int pre_a, int b_post) {
// CHECK: Decision,File 0, [[@LINE+2]]:11 -> [[@LINE+3]]:18 = M:0, C:2
// CHECK: Branch,File 0, [[@LINE+1]]:11 -> [[@LINE+1]]:16 = (#0 - #1), #1 [1,0,2]
return (pre_a
|| POST(b));
// CHECK: Expansion,File 0, [[@LINE-1]]:14 -> [[@LINE-1]]:18 = #1 (Expanded file = 1)
// CHECK: Branch,File 1, [[@LINE-9]]:17 -> [[@LINE-9]]:20 = (#1 - #2), #2 [2,0,0]
}

#define bar_post b_post

// CHECK: post1:
int post1(int pre_a, int b_post) {
// CHECK: Decision,File 0, [[@LINE+3]]:11 -> [[@LINE+4]]:18 = M:0, C:2
// CHECK: Branch,File 0, [[@LINE+2]]:11 -> [[@LINE+2]]:16 = (#0 - #1), #1 [1,0,2]
// CHECK: Expansion,File 0, [[@LINE+2]]:14 -> [[@LINE+2]]:18 = 0 (Expanded file = 1)
return (pre_a
|| POST(bar));
// CHECK: Expansion,File 1, 42:17 -> 42:18 = #1 (Expanded file = 2)
// CHECK: Branch,File 2, 54:18 -> 54:24 = (#1 - #2), #2 [2,0,0]
}
50 changes: 50 additions & 0 deletions clang/test/CoverageMapping/mcdc-system-headers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// RUN: %clang_cc1 -std=c++11 -triple %itanium_abi_triple -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -fcoverage-mcdc -mllvm -system-headers-coverage -emit-llvm-only -o - %s | FileCheck %s --check-prefixes=CHECK,W_SYS
// RUN: %clang_cc1 -std=c++11 -triple %itanium_abi_triple -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -fcoverage-mcdc -emit-llvm-only -o - %s | FileCheck %s --check-prefixes=CHECK,X_SYS

#ifdef IS_SYSHEADER

#pragma clang system_header
#define CONST 42
#define EXPR1(x) (x)
#define EXPR2(x) ((x) && (x))

#else

#define IS_SYSHEADER
#include __FILE__

// CHECK: _Z5func0i:
int func0(int a) {
// CHECK: Decision,File 0, [[@LINE+3]]:11 -> [[@LINE+3]]:21 = M:0, C:2
// W_SYS: Expansion,File 0, [[@LINE+2]]:11 -> [[@LINE+2]]:16 = #0 (Expanded file = 1)
// X_SYS: Branch,File 0, [[@LINE+1]]:11 -> [[@LINE+1]]:11 = 0, 0 [1,2,0]
return (CONST && a);
// CHECK: Branch,File 0, [[@LINE-1]]:20 -> [[@LINE-1]]:21 = #2, (#1 - #2) [2,0,0]
// W_SYS: Branch,File 1, [[@LINE-16]]:15 -> [[@LINE-16]]:17 = 0, 0 [1,2,0]
}

// CHECK: _Z5func1ii:
int func1(int a, int b) {
// CHECK: Decision,File 0, [[@LINE+2]]:11 -> [[@LINE+2]]:21 = M:0, C:2
// CHECK: Branch,File 0, [[@LINE+1]]:11 -> [[@LINE+1]]:12 = (#0 - #1), #1 [1,0,2]
return (a || EXPR1(b));
// W_SYS: Expansion,File 0, [[@LINE-1]]:16 -> [[@LINE-1]]:21 = #1 (Expanded file = 1)
// W_SYS: Branch,File 1, [[@LINE-24]]:18 -> [[@LINE-24]]:21 = (#1 - #2), #2 [2,0,0]
// X_SYS: Branch,File 0, [[@LINE-3]]:16 -> [[@LINE-3]]:16 = (#1 - #2), #2 [2,0,0]
}

// CHECK: _Z5func2ii:
int func2(int a, int b) {
// W_SYS: Decision,File 0, [[@LINE+5]]:11 -> [[@LINE+5]]:28 = M:0, C:3
// X_SYS: Decision,File 0, [[@LINE+4]]:11 -> [[@LINE+4]]:28 = M:0, C:2
// W_SYS: Expansion,File 0, [[@LINE+3]]:11 -> [[@LINE+3]]:16 = #0 (Expanded file = 1)
// W_SYS: Expansion,File 0, [[@LINE+2]]:23 -> [[@LINE+2]]:28 = #1 (Expanded file = 2)
// X_SYS: Branch,File 0, [[@LINE+1]]:11 -> [[@LINE+1]]:11 = #1, (#0 - #1) [1,2,0]
return (EXPR2(a) && EXPR1(a));
// W_SYS: Branch,File 1, [[@LINE-35]]:19 -> [[@LINE-35]]:22 = #3, (#0 - #3) [1,3,0]
// W_SYS: Branch,File 1, [[@LINE-36]]:26 -> [[@LINE-36]]:29 = #4, (#3 - #4) [3,2,0]
// W_SYS: Branch,File 2, [[@LINE-38]]:18 -> [[@LINE-38]]:21 = #2, (#1 - #2) [2,0,0]
// X_SYS: Branch,File 0, [[@LINE-4]]:23 -> [[@LINE-4]]:23 = #2, (#1 - #2) [2,0,0]
}

#endif
Loading