/// \brief The region's ending location.
Optional<SourceLocation> LocEnd;
+ /// Whether this region should be emitted after its parent is emitted.
+ bool DeferRegion;
+
public:
SourceMappingRegion(Counter Count, Optional<SourceLocation> LocStart,
- Optional<SourceLocation> LocEnd)
- : Count(Count), LocStart(LocStart), LocEnd(LocEnd) {}
+ Optional<SourceLocation> LocEnd, bool DeferRegion = false)
+ : Count(Count), LocStart(LocStart), LocEnd(LocEnd),
+ DeferRegion(DeferRegion) {}
const Counter &getCounter() const { return Count; }
assert(LocEnd && "Region has no end location");
return *LocEnd;
}
+
+ bool isDeferred() const { return DeferRegion; }
+
+ void setDeferred(bool Deferred) { DeferRegion = Deferred; }
};
/// Spelling locations for the start and end of a source region.
/// \brief A stack of currently live regions.
std::vector<SourceMappingRegion> RegionStack;
+ /// The currently deferred region: its end location and count can be set once
+ /// its parent has been popped from the region stack.
+ Optional<SourceMappingRegion> DeferredRegion;
+
CounterExpressionBuilder Builder;
/// \brief A location in the most recently visited file or macro.
/// used with popRegions to exit a "scope", ending the region that was pushed.
size_t pushRegion(Counter Count, Optional<SourceLocation> StartLoc = None,
Optional<SourceLocation> EndLoc = None) {
- if (StartLoc)
+ if (StartLoc) {
MostRecentLocation = *StartLoc;
+ completeDeferred(Count, MostRecentLocation);
+ }
RegionStack.emplace_back(Count, StartLoc, EndLoc);
return RegionStack.size() - 1;
}
+ /// Complete any pending deferred region by setting its end location and
+ /// count, and then pushing it onto the region stack.
+ size_t completeDeferred(Counter Count, SourceLocation DeferredEndLoc) {
+ size_t Index = RegionStack.size();
+ if (!DeferredRegion)
+ return Index;
+
+ // Consume the pending region.
+ SourceMappingRegion DR = DeferredRegion.getValue();
+ DeferredRegion = None;
+
+ // If the region ends in an expansion, find the expansion site.
+ if (SM.getFileID(DeferredEndLoc) != SM.getMainFileID()) {
+ FileID StartFile = SM.getFileID(DR.getStartLoc());
+ if (isNestedIn(DeferredEndLoc, StartFile)) {
+ do {
+ DeferredEndLoc = getIncludeOrExpansionLoc(DeferredEndLoc);
+ } while (StartFile != SM.getFileID(DeferredEndLoc));
+ }
+ }
+
+ // The parent of this deferred region ends where the containing decl ends,
+ // so the region isn't useful.
+ if (DR.getStartLoc() == DeferredEndLoc)
+ return Index;
+
+ // If we're visiting statements in non-source order (e.g switch cases or
+ // a loop condition) we can't construct a sensible deferred region.
+ if (!SpellingRegion(SM, DR.getStartLoc(), DeferredEndLoc).isInSourceOrder())
+ return Index;
+
+ DR.setCounter(Count);
+ DR.setEndLoc(DeferredEndLoc);
+ handleFileExit(DeferredEndLoc);
+ RegionStack.push_back(DR);
+ return Index;
+ }
+
/// \brief Pop regions from the stack into the function's list of regions.
///
/// Adds all regions from \c ParentIndex to the top of the stack to the
/// function's \c SourceRegions.
void popRegions(size_t ParentIndex) {
assert(RegionStack.size() >= ParentIndex && "parent not in stack");
+ bool ParentOfDeferredRegion = false;
while (RegionStack.size() > ParentIndex) {
SourceMappingRegion &Region = RegionStack.back();
if (Region.hasStartLoc()) {
assert(SM.isWrittenInSameFile(Region.getStartLoc(), EndLoc));
SourceRegions.push_back(Region);
+
+ if (ParentOfDeferredRegion) {
+ ParentOfDeferredRegion = false;
+
+ // If there's an existing deferred region, keep the old one, because
+ // it means there are two consecutive returns (or a similar pattern).
+ if (!DeferredRegion.hasValue() &&
+ // File IDs aren't gathered within macro expansions, so it isn't
+ // useful to try and create a deferred region inside of one.
+ (SM.getFileID(EndLoc) == SM.getMainFileID()))
+ DeferredRegion =
+ SourceMappingRegion(Counter::getZero(), EndLoc, None);
+ }
+ } else if (Region.isDeferred()) {
+ assert(!ParentOfDeferredRegion && "Consecutive deferred regions");
+ ParentOfDeferredRegion = true;
}
RegionStack.pop_back();
}
+ assert(!ParentOfDeferredRegion && "Deferred region with no parent");
}
/// \brief Return the currently active region.
handleFileExit(StartLoc);
if (!Region.hasStartLoc())
Region.setStartLoc(StartLoc);
+
+ completeDeferred(Region.getCounter(), StartLoc);
}
/// \brief Mark \c S as a terminator, starting a zero region.
if (!Region.hasEndLoc())
Region.setEndLoc(getEnd(S));
pushRegion(Counter::getZero());
+ getRegion().setDeferred(true);
}
/// \brief Keep counts of breaks and continues inside loops.
CoverageMappingModuleGen &CVM,
llvm::DenseMap<const Stmt *, unsigned> &CounterMap, SourceManager &SM,
const LangOptions &LangOpts)
- : CoverageMappingBuilder(CVM, SM, LangOpts), CounterMap(CounterMap) {}
+ : CoverageMappingBuilder(CVM, SM, LangOpts), CounterMap(CounterMap),
+ DeferredRegion(None) {}
/// \brief Write the mapping data to the output stream
void write(llvm::raw_ostream &OS) {
llvm::SmallVector<unsigned, 8> VirtualFileMapping;
gatherFileIDs(VirtualFileMapping);
SourceRegionFilter Filter = emitExpansionRegions();
+ assert(!DeferredRegion && "Deferred region never completed");
emitSourceRegions(Filter);
gatherSkippedRegions();
}
void VisitDecl(const Decl *D) {
+ assert(!DeferredRegion && "Deferred region never completed");
+
Stmt *Body = D->getBody();
// Do not propagate region counts into system headers.
if (Body && SM.isInSystemHeader(SM.getSpellingLoc(getStart(Body))))
return;
- propagateCounts(getRegionCounter(Body), Body);
+ Counter ExitCount = propagateCounts(getRegionCounter(Body), Body);
+ assert(RegionStack.empty() && "Regions entered but never exited");
+
+ // Complete any deferred regions introduced by the last statement in a decl.
+ popRegions(completeDeferred(ExitCount, getEnd(Body)));
}
void VisitReturnStmt(const ReturnStmt *S) {
--- /dev/null
+// RUN: %clang_cc1 -std=c++11 -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -fexceptions -fcxx-exceptions -emit-llvm-only -triple %itanium_abi_triple -main-file-name deferred-region.cpp %s | FileCheck %s
+
+#define IF if
+#define STMT(S) S
+
+// CHECK-LABEL: _Z3fooi:
+void foo(int x) {
+ if (x == 0) {
+ return;
+ } // CHECK: [[@LINE]]:4 -> [[@LINE+2]]:2 = (#0 - #1)
+
+}
+
+// CHECK-NEXT: _Z4foooi:
+void fooo(int x) {
+ if (x == 0) {
+ return;
+ } // CHECK: [[@LINE]]:4 -> [[@LINE+2]]:3 = (#0 - #1)
+
+ if (x == 1) {
+ return;
+ } // CHECK: [[@LINE]]:4 -> [[@LINE+2]]:2 = ((#0 - #1) - #2)
+
+}
+
+// CHECK-LABEL: _Z3bazv:
+void baz() { // CHECK: [[@LINE]]:12 -> [[@LINE+2]]:2
+ return; // CHECK-NOT: File
+}
+
+// CHECK-LABEL: _Z3bari:
+void bar(int x) {
+ IF (x)
+ return; // CHECK: [[@LINE]]:11 -> [[@LINE+2]]:3 = (#0 - #1)
+
+ IF (!x)
+ return; // CHECK: [[@LINE]]:11 -> [[@LINE+2]]:3 = ((#0 - #1) - #2)
+
+ foo(x);
+}
+
+// CHECK-LABEL: _Z4quuxi:
+// Deferred regions are not emitted within macro expansions.
+void quux(int x) {
+ STMT(
+ if (x == 0)
+ return;)
+
+ // CHECK-NOT: [[@LINE-2]]:{{.*}} -> [[@LINE+2]]
+
+ if (x == 1)
+ STMT(return;)
+
+ // CHECK-NOT: [[@LINE-2]]:{{.*}} -> [[@LINE+3]]
+
+ STMT(
+ if (x == 2)
+ return;
+
+ // CHECK-NOT: [[@LINE-2]]:{{.*}} -> [[@LINE+2]]
+
+ if (x == 3)
+ return;
+ )
+}
+
+// CHECK-LABEL: _Z8weird_ifv:
+void weird_if() {
+ int i = 0;
+
+ if (false)
+ return; // CHECK: [[@LINE]]:11 -> [[@LINE+2]]:3 = (#0 - #1)
+
+ if (false)
+ i++;
+
+ if (i + 100 > 0) { // CHECK: [[@LINE]]:20 -> [[@LINE+6]]:4 = #3
+ if (false) // CHECK: [[@LINE+1]]:7 -> [[@LINE+1]]:13 = #4
+ return; // CHECK: [[@LINE]]:13 -> [[@LINE+2]]:5 = (#3 - #4)
+ // CHECK: [[@LINE+1]]:5 -> [[@LINE+3]]:4 = (#3 - #4)
+ return; // CHECK: [[@LINE]]:5 -> [[@LINE+4]]:3 = ((#0 - #1) - #3)
+
+ }
+
+ if (false)
+ return;
+}
+
+// CHECK-LABEL: _Z8for_loopv:
+void for_loop() {
+ if (false)
+ return; // CHECK: [[@LINE]]:11 -> [[@LINE+2]]:3 = (#0 - #1)
+
+ for (int i = 0; i < 10; ++i) {
+ if (i % 2 == 0)
+ continue; // CHECK: [[@LINE]]:15 -> [[@LINE+2]]:5 = (#2 - #3)
+
+ if (i % 5 == 0)
+ break; // CHECK: [[@LINE]]:12 -> [[@LINE+2]]:5 = ((#2 - #3) - #4)
+
+ int x = i; // CHECK: [[@LINE]]:5 -> [[@LINE+3]]:4 = ((#2 - #3) - #4)
+ return; // CHECK-NOT: [[@LINE]]:11 -> [[@LINE+2]]
+
+ }
+}
+
+struct Error {};
+
+// CHECK-LABEL: _Z10while_loopv:
+void while_loop() {
+ if (false)
+ return; // CHECK: [[@LINE]]:11 -> [[@LINE+2]]:3 = (#0 - #1)
+
+ int x = 0;
+ while (++x < 10) {
+ if (x == 1)
+ continue; // CHECK: [[@LINE]]:15 -> [[@LINE+2]]:5 = (#2 - #3)
+
+ while (++x < 4) {
+ if (x == 3)
+ break; // CHECK: [[@LINE]]:14 -> [[@LINE+2]]:7 = (#4 - #5)
+
+ while (++x < 5) {}
+ }
+
+ if (x == 0)
+ throw Error(); // CHECK: [[@LINE]]:20 -> [[@LINE+2]]:5 = ((#2 - #3) - #7)
+
+ while (++x < 9) {
+ if (x == 0)
+ break; // CHECK-NOT: [[@LINE]]:14 -> [[@LINE+2]]
+
+ }
+ }
+}
+
+// CHECK-LABEL: _Z5gotosv:
+void gotos() {
+ if (false)
+ goto out; // CHECK: [[@LINE]]:13 -> [[@LINE+2]]:3 = (#0 - #1)
+
+ return; // CHECK: [[@LINE]]:3 -> [[@LINE+4]]:2 = (#0 - #1)
+
+out:
+ return; // CHECK: [[@LINE]]:8 -> [[@LINE+1]]:2 = 0
+}
+
+int main() {
+ foo(0);
+ foo(1);
+ fooo(0);
+ fooo(1);
+ baz();
+ bar(0);
+ bar(1);
+ quux(0);
+ quux(1);
+ quux(2);
+ quux(3);
+ weird_if();
+ for_loop();
+ while_loop();
+ gotos();
+ return 0;
+}
int m = 2;
} else
goto x; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:13 = (#1 - #2)
- int k = 3; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE+1]]:4 = #3
- }
- static int j = 0; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+4]]:2 = ((#0 + #3) - #1)
+ int k = 3; // CHECK-NEXT: File 0, [[@LINE-1]]:13 -> [[@LINE]]:5 = #3
+ } // CHECK-NEXT: File 0, [[@LINE-1]]:5 -> [[@LINE]]:4 = #3
+ static int j = 0; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+5]]:2 = ((#0 + #3) - #1)
++j;
if(j == 1) // CHECK-NEXT: File 0, [[@LINE]]:6 -> [[@LINE]]:12 = ((#0 + #3) - #1)
goto x; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:11 = #4
+ // CHECK-NEXT: File 0, [[@LINE-1]]:11 -> [[@LINE+1]]:2 = (((#0 + #3) - #1) - #4)
}
// CHECK-NEXT: test1
void test1(int x) { // CHECK-NEXT: File 0, [[@LINE]]:19 -> {{[0-9]+}}:2 = #0
if(x == 0) // CHECK-NEXT: File 0, [[@LINE]]:6 -> [[@LINE]]:12 = #0
goto a; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:11 = #1
- goto b; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE]]:9 = (#0 - #1)
+ // CHECK-NEXT: File 0, [[@LINE-1]]:11 -> [[@LINE+1]]:3 = (#0 - #1)
+ goto b; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+5]]:2 = (#0 - #1)
+ // CHECK-NEXT: File 0, [[@LINE-1]]:3 -> [[@LINE+4]]:2 = #3
a: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE+3]]:2 = #2
b: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE+2]]:2 = #3
x = x + 1;
void test2(int x) { // CHECK-NEXT: File 0, [[@LINE]]:19 -> {{[0-9]+}}:2 = #0
if(x == 0) // CHECK-NEXT: File 0, [[@LINE]]:6 -> [[@LINE]]:12 = #0
goto a; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:11 = #1
- // CHECK-NEXT: File 0, [[@LINE+2]]:8 -> [[@LINE+2]]:25 = (#0 - #1)
+ // CHECK-NEXT: File 0, [[@LINE-1]]:11 -> [[@LINE+3]]:8 = #0
+ // CHECK-NEXT: File 0, [[@LINE+2]]:8 -> [[@LINE+3]]:11 = (#0 - #1)
// CHECK-NEXT: File 0, [[@LINE+1]]:11 -> [[@LINE+1]]:17 = (#0 - #1)
- else if(x == 1) goto b; // CHECK-NEXT: File 0, [[@LINE]]:19 -> [[@LINE]]:25 = #2
+ else if(x == 1) // CHECK-NEXT: File 0, [[@LINE+1]]:5 -> [[@LINE+1]]:11 = #2
+ goto b; // CHECK-NEXT: File 0, [[@LINE]]:11 -> [[@LINE+1]]:1 = #3
a: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE+3]]:2 = #3
b: // CHECK-NEXT: File 0, [[@LINE]]:1 -> [[@LINE+2]]:2 = #4
x = x + 1;
// CHECK-NEXT: main
int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> {{[0-9]+}}:2 = #0
int j = 0;
- for(int i = 0; i < 10; ++i) { // CHECK: File 0, [[@LINE]]:31 -> [[@LINE+11]]:4 = #1
- a: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+10]]:4 = #2
+ for(int i = 0; i < 10; ++i) { // CHECK: File 0, [[@LINE]]:31 -> [[@LINE+13]]:4 = #1
+ a: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+12]]:4 = #2
if(i < 3) // CHECK-NEXT: File 0, [[@LINE]]:8 -> [[@LINE]]:13 = #2
goto e; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:13 = #3
- goto c; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:11 = (#2 - #3)
+ // CHECK-NEXT: File 0, [[@LINE-1]]:13 -> [[@LINE+1]]:5 = (#2 - #3)
+ goto c; // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE+8]]:4 = (#2 - #3)
+
b: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+6]]:4 = #4
j = 2;
c: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+4]]:4 = #5
if (!argc) LBRAC
return 0;
// CHECK-NEXT: Expansion,File 0, [[@LINE+1]]:3 -> [[@LINE+1]]:8 = #2
- RBRAC
+ RBRAC // CHECK-NEXT: [[@LINE]]:8 -> [[@LINE+6]]:3 = (#0 - #2)
// CHECK-NEXT: File 0, [[@LINE+4]]:3 -> [[@LINE+15]]:2 = (#0 - #2)
// CHECK-NEXT: File 0, [[@LINE+3]]:7 -> [[@LINE+3]]:12 = (#0 - #2)
// CHECK-NEXT: File 0, [[@LINE+1]]:19 -> [[@LINE+3]]:4 = #3
if (!argc) LBRAC
return 0;
- }
+ } // CHECK-NEXT: [[@LINE]]:4 -> [[@LINE+5]]:3 = ((#0 - #2) - #3)
// CHECK-NEXT: File 0, [[@LINE+3]]:3 -> [[@LINE+7]]:2 = ((#0 - #2) - #3)
// CHECK-NEXT: File 0, [[@LINE+2]]:7 -> [[@LINE+2]]:12 = ((#0 - #2) - #3)
if (!argc) {
return 0;
// CHECK-NEXT: Expansion,File 0, [[@LINE+1]]:3 -> [[@LINE+1]]:8 = #4
- RBRAC
+ RBRAC // CHECK-NEXT: [[@LINE]]:8 -> [[@LINE+1]]:2 = (((#0 - #2) - #3) - #4)
}
// CHECK-NEXT: File 1, 3:15 -> 3:16 = #2
for(int i = 0; i < 10; ++i) { // CHECK-NEXT: File 0, [[@LINE]]:31 -> {{[0-9]+}}:4 = #1
// CHECK-NEXT: File 0, [[@LINE+1]]:8 -> [[@LINE+1]]:13 = #1
if(i > 2) { // CHECK-NEXT: File 0, [[@LINE]]:15 -> [[@LINE+2]]:6 = #2
- return;
+ return; // CHECK-NEXT: File 0, [[@LINE+1]]:6 -> [[@LINE+3]]:5 = (#1 - #2)
} // CHECK-NEXT: File 0, [[@LINE+2]]:5 -> {{[0-9]+}}:4 = (#1 - #2)
// CHECK-NEXT: File 0, [[@LINE+1]]:8 -> [[@LINE+1]]:14 = (#1 - #2)
if(i == 3) { // CHECK-NEXT: File 0, [[@LINE]]:16 -> [[@LINE+2]]:6 = #3
case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:11 = #2
return;
case 2: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #3
- break;
+ break; // CHECK-NEXT: File 0, [[@LINE]]:10 -> [[@LINE+2]]:3 = #1
}
int x = 0; // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:2 = #1
}
i = 2;
break;
default: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = #4
- break;
+ break; // CHECK-NEXT: File 0, [[@LINE]]:10 -> [[@LINE+2]]:3 = #1
}
switch(i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+23]]:2 = #1
case 0: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+2]]:10 = #6
case 1: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+3]]:10 = #7
i = 2;
default: // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+1]]:10 = (#7 + #8)
- break;
+ break; // CHECK-NEXT: File 0, [[@LINE]]:10 -> [[@LINE+3]]:3 = #5
}
switch(i) { // CHECK-NEXT: File 0, [[@LINE]]:3 -> [[@LINE+13]]:2 = #5
default: // CHECK-NEXT: File 0, [[@LINE]]:3 -> {{[0-9]+}}:11 = #2
if (i == 1) // CHECK-NEXT: File 0, [[@LINE]]:9 -> [[@LINE]]:15 = #2
return 0; // CHECK-NEXT: File 0, [[@LINE]]:7 -> [[@LINE]]:15 = #3
+ // CHECK-NEXT: File 0, [[@LINE-1]]:15 -> [[@LINE+3]]:5 = (#2 - #3)
// CHECK-NEXT: Expansion,File 0, [[@LINE+2]]:5 -> [[@LINE+2]]:8 = (#2 - #3) (Expanded file = 1)
// CHECK-NEXT: File 0, [[@LINE+1]]:8 -> {{[0-9]+}}:11 = (#2 - #3)
FOO(1);
// CHECK-NEXT: File 0, [[@LINE+1]]:10 -> [[@LINE+2]]:27 = (#0 - #1)
} else if(i == 8) // CHECK-NEXT: File 0, [[@LINE]]:13 -> [[@LINE]]:19 = (#0 - #1)
throw ImportantError(); // CHECK-NEXT: File 0, [[@LINE]]:5 -> [[@LINE]]:27 = #2
-}
+} // CHECK-NEXT: File 0, [[@LINE-1]]:27 -> [[@LINE]]:2 = ((#0 - #1) - #2)
// CHECK-NEXT: main
int main() { // CHECK-NEXT: File 0, [[@LINE]]:12 -> [[@LINE+13]]:2 = #0