mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[Profiler] Rework profiling of top-level code
Instead of creating and destroying a SILProfiler per TopLevelCodeDecl, setup a single profiler for the top-level entry point function, and visit all the TopLevelCodeDecls when mapping regions.
This commit is contained in:
@@ -576,9 +576,7 @@ public:
|
||||
Profiler = InheritedProfiler;
|
||||
}
|
||||
|
||||
void createProfiler(ASTNode Root, SILDeclRef Ref);
|
||||
|
||||
void discardProfiler() { Profiler = nullptr; }
|
||||
void createProfiler(SILDeclRef Ref);
|
||||
|
||||
ProfileCounter getEntryCount() const { return EntryCount; }
|
||||
|
||||
|
||||
@@ -83,8 +83,6 @@ class SILProfiler : public SILAllocated<SILProfiler> {
|
||||
private:
|
||||
SILModule &M;
|
||||
|
||||
ASTNode Root;
|
||||
|
||||
SILDeclRef forDecl;
|
||||
|
||||
bool EmitCoverageMapping;
|
||||
@@ -107,13 +105,11 @@ private:
|
||||
|
||||
std::vector<std::tuple<std::string, uint64_t, std::string>> CoverageData;
|
||||
|
||||
SILProfiler(SILModule &M, ASTNode Root, SILDeclRef forDecl,
|
||||
bool EmitCoverageMapping)
|
||||
: M(M), Root(Root), forDecl(forDecl),
|
||||
EmitCoverageMapping(EmitCoverageMapping) {}
|
||||
SILProfiler(SILModule &M, SILDeclRef forDecl, bool EmitCoverageMapping)
|
||||
: M(M), forDecl(forDecl), EmitCoverageMapping(EmitCoverageMapping) {}
|
||||
|
||||
public:
|
||||
static SILProfiler *create(SILModule &M, ASTNode N, SILDeclRef Ref);
|
||||
static SILProfiler *create(SILModule &M, SILDeclRef Ref);
|
||||
|
||||
/// Check if the function is set up for profiling.
|
||||
bool hasRegionCounters() const { return NumRegionCounters != 0; }
|
||||
|
||||
@@ -332,12 +332,11 @@ void SILFunction::deleteSnapshot(int ID) {
|
||||
} while ((f = f->snapshots) != nullptr);
|
||||
}
|
||||
|
||||
void SILFunction::createProfiler(ASTNode Root, SILDeclRef Ref) {
|
||||
void SILFunction::createProfiler(SILDeclRef Ref) {
|
||||
assert(!Profiler && "Function already has a profiler");
|
||||
assert(Root && "Cannot profile a null ASTNode");
|
||||
assert(Ref && "Must have non-null SILDeclRef");
|
||||
|
||||
Profiler = SILProfiler::create(Module, Root, Ref);
|
||||
Profiler = SILProfiler::create(Module, Ref);
|
||||
if (!Profiler)
|
||||
return;
|
||||
|
||||
|
||||
@@ -25,13 +25,75 @@
|
||||
|
||||
using namespace swift;
|
||||
|
||||
/// Check whether a root AST node should be profiled.
|
||||
static bool shouldProfile(ASTNode N, SILDeclRef Constant) {
|
||||
/// Unfortunately this is needed as ASTNode can't currently represent a
|
||||
/// SourceFile.
|
||||
class NodeToProfile final {
|
||||
/// For a direct ASTNode, this stores the node itself. For a main SourceFile,
|
||||
/// it stores the corresponding ModuleDecl.
|
||||
ASTNode Storage;
|
||||
|
||||
explicit NodeToProfile(ASTNode Node) : Storage(Node) {}
|
||||
|
||||
public:
|
||||
static NodeToProfile node(ASTNode Node) {
|
||||
assert(!isa_and_nonnull<ModuleDecl>(Node.dyn_cast<Decl *>()));
|
||||
return NodeToProfile(Node);
|
||||
}
|
||||
static NodeToProfile mainSourceFile(SourceFile *SF) {
|
||||
assert(SF->isScriptMode());
|
||||
auto N = NodeToProfile(SF->getParentModule());
|
||||
assert(N.getAsSourceFile() == SF);
|
||||
return N;
|
||||
}
|
||||
|
||||
/// If an ASTNode is being stored, returns it, otherwise \c nullptr.
|
||||
ASTNode getAsNode() const {
|
||||
return isSourceFile() ? nullptr : Storage;
|
||||
}
|
||||
|
||||
/// Whether this is storing a main SourceFile.
|
||||
bool isSourceFile() const {
|
||||
return getAsSourceFile();
|
||||
}
|
||||
|
||||
/// If a main SourceFile is being stored, returns it, otherwise \c nullptr.
|
||||
SourceFile *getAsSourceFile() const {
|
||||
auto *M = dyn_cast_or_null<ModuleDecl>(Storage.dyn_cast<Decl *>());
|
||||
return M ? &M->getMainSourceFile() : nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
static NodeToProfile getNodeToProfile(SILDeclRef Constant) {
|
||||
// If we have an initialization expression, walk that instead of the variable.
|
||||
if (auto *E = Constant.getInitializationExpr())
|
||||
return NodeToProfile::node(E);
|
||||
|
||||
// Otherwise, we walk the SILDeclRef's node directly.
|
||||
using LocKind = SILDeclRef::LocKind;
|
||||
switch (Constant.getLocKind()) {
|
||||
case LocKind::Decl:
|
||||
return NodeToProfile::node(Constant.getDecl());
|
||||
case LocKind::Closure:
|
||||
return NodeToProfile::node(Constant.getAbstractClosureExpr());
|
||||
case LocKind::File: {
|
||||
auto *SF = cast<SourceFile>(Constant.getFileUnit());
|
||||
return NodeToProfile::mainSourceFile(SF);
|
||||
}
|
||||
}
|
||||
llvm_unreachable("Unhandled case in switch!");
|
||||
}
|
||||
|
||||
/// Check whether we should profile a given SILDeclRef.
|
||||
static bool shouldProfile(SILDeclRef Constant) {
|
||||
auto Root = getNodeToProfile(Constant);
|
||||
|
||||
// Do not profile AST nodes with invalid source locations.
|
||||
if (N.getStartLoc().isInvalid() || N.getEndLoc().isInvalid()) {
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< "Skipping ASTNode: invalid start/end locations\n");
|
||||
return false;
|
||||
if (auto N = Root.getAsNode()) {
|
||||
if (N.getStartLoc().isInvalid() || N.getEndLoc().isInvalid()) {
|
||||
LLVM_DEBUG(llvm::dbgs()
|
||||
<< "Skipping ASTNode: invalid start/end locations\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not profile AST nodes in unavailable contexts.
|
||||
@@ -62,18 +124,17 @@ static Stmt *getProfilerStmtForCase(CaseStmt *caseStmt) {
|
||||
llvm_unreachable("invalid parent kind");
|
||||
}
|
||||
|
||||
SILProfiler *SILProfiler::create(SILModule &M, ASTNode N, SILDeclRef Ref) {
|
||||
SILProfiler *SILProfiler::create(SILModule &M, SILDeclRef Ref) {
|
||||
// If profiling isn't enabled, don't profile anything.
|
||||
const auto &Opts = M.getOptions();
|
||||
if (!Opts.GenerateProfile && Opts.UseProfile.empty())
|
||||
return nullptr;
|
||||
|
||||
if (!shouldProfile(N, Ref))
|
||||
if (!shouldProfile(Ref))
|
||||
return nullptr;
|
||||
|
||||
auto *Buf = M.allocate<SILProfiler>(1);
|
||||
auto *SP =
|
||||
::new (Buf) SILProfiler(M, N, Ref, Opts.EmitProfileCoverageMapping);
|
||||
auto *SP = ::new (Buf) SILProfiler(M, Ref, Opts.EmitProfileCoverageMapping);
|
||||
SP->assignRegionCounters();
|
||||
return SP;
|
||||
}
|
||||
@@ -1277,6 +1338,16 @@ static StringRef getCurrentFileName(SILDeclRef forDecl) {
|
||||
return {};
|
||||
}
|
||||
|
||||
static void walkNode(NodeToProfile Node, ASTWalker &Walker) {
|
||||
if (auto N = Node.getAsNode()) {
|
||||
N.walk(Walker);
|
||||
} else {
|
||||
// We want to walk the SourceFile for a top-level entry point. We will only
|
||||
// assign regions to TopLevelCodeDecls.
|
||||
Node.getAsSourceFile()->walk(Walker);
|
||||
}
|
||||
}
|
||||
|
||||
void SILProfiler::assignRegionCounters() {
|
||||
const auto &SM = M.getASTContext().SourceMgr;
|
||||
|
||||
@@ -1284,22 +1355,16 @@ void SILProfiler::assignRegionCounters() {
|
||||
|
||||
MapRegionCounters Mapper(forDecl, RegionCounterMap);
|
||||
|
||||
std::string CurrentFuncName;
|
||||
FormalLinkage CurrentFuncLinkage;
|
||||
if (auto *D = Root.dyn_cast<Decl *>()) {
|
||||
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
|
||||
CurrentFuncName = forDecl.mangle();
|
||||
CurrentFuncLinkage = getDeclLinkage(AFD);
|
||||
} else {
|
||||
auto *TLCD = cast<TopLevelCodeDecl>(D);
|
||||
llvm::raw_string_ostream OS{CurrentFuncName};
|
||||
OS << "__tlcd_";
|
||||
TLCD->getStartLoc().printLineAndColumn(OS, SM);
|
||||
CurrentFuncLinkage = FormalLinkage::HiddenUnique;
|
||||
auto Root = getNodeToProfile(forDecl);
|
||||
|
||||
auto CurrentFuncName = forDecl.mangle();
|
||||
auto CurrentFuncLinkage = FormalLinkage::HiddenUnique;
|
||||
|
||||
if (auto N = Root.getAsNode()) {
|
||||
if (auto *D = N.dyn_cast<Decl *>()) {
|
||||
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D))
|
||||
CurrentFuncLinkage = getDeclLinkage(AFD);
|
||||
}
|
||||
} else {
|
||||
CurrentFuncName = forDecl.mangle();
|
||||
CurrentFuncLinkage = FormalLinkage::HiddenUnique;
|
||||
}
|
||||
|
||||
PGOFuncName = llvm::getPGOFuncName(
|
||||
@@ -1311,7 +1376,7 @@ void SILProfiler::assignRegionCounters() {
|
||||
|
||||
LLVM_DEBUG(llvm::dbgs() << "Assigning counters to: " << CurrentFuncName
|
||||
<< "\n");
|
||||
Root.walk(Mapper);
|
||||
walkNode(Root, Mapper);
|
||||
|
||||
NumRegionCounters = Mapper.NextCounter;
|
||||
// TODO: Mapper needs to calculate a function hash as it goes.
|
||||
@@ -1319,7 +1384,7 @@ void SILProfiler::assignRegionCounters() {
|
||||
|
||||
if (EmitCoverageMapping) {
|
||||
CoverageMapping Coverage(SM, forDecl);
|
||||
Root.walk(Coverage);
|
||||
walkNode(Root, Coverage);
|
||||
CovMap =
|
||||
Coverage.emitSourceRegions(M, CurrentFuncName, PGOFuncName, PGOFuncHash,
|
||||
RegionCounterMap, CurrentFileName);
|
||||
@@ -1337,7 +1402,7 @@ void SILProfiler::assignRegionCounters() {
|
||||
}
|
||||
PGOMapping pgoMapper(forDecl, RegionCounterMap, LoadedCounts.get(),
|
||||
RegionLoadedCounterMap, RegionCondToParentMap);
|
||||
Root.walk(pgoMapper);
|
||||
walkNode(Root, pgoMapper);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -856,7 +856,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
|
||||
if (auto *ce = constant.getAbstractClosureExpr()) {
|
||||
preEmitFunction(constant, f, ce);
|
||||
PrettyStackTraceSILFunction X("silgen closureexpr", f);
|
||||
f->createProfiler(ce, constant);
|
||||
f->createProfiler(constant);
|
||||
SILGenFunction(*this, *f, ce).emitClosure(ce);
|
||||
postEmitFunction(constant, f);
|
||||
break;
|
||||
@@ -866,7 +866,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
|
||||
|
||||
preEmitFunction(constant, f, fd);
|
||||
PrettyStackTraceSILFunction X("silgen emitFunction", f);
|
||||
f->createProfiler(fd, constant);
|
||||
f->createProfiler(constant);
|
||||
SILGenFunction(*this, *f, fd).emitFunction(fd);
|
||||
postEmitFunction(constant, f);
|
||||
break;
|
||||
@@ -885,7 +885,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
|
||||
} else {
|
||||
preEmitFunction(constant, f, decl);
|
||||
PrettyStackTraceSILFunction X("silgen emitConstructor", f);
|
||||
f->createProfiler(decl, constant);
|
||||
f->createProfiler(constant);
|
||||
SILGenFunction(*this, *f, decl).emitValueConstructor(decl);
|
||||
postEmitFunction(constant, f);
|
||||
}
|
||||
@@ -898,7 +898,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
|
||||
|
||||
preEmitFunction(constant, f, decl);
|
||||
PrettyStackTraceSILFunction X("silgen constructor initializer", f);
|
||||
f->createProfiler(decl, constant);
|
||||
f->createProfiler(constant);
|
||||
SILGenFunction(*this, *f, decl).emitClassConstructorInitializer(decl);
|
||||
postEmitFunction(constant, f);
|
||||
break;
|
||||
@@ -952,7 +952,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
|
||||
auto loc = RegularLocation::getAutoGeneratedLocation(init);
|
||||
preEmitFunction(constant, f, loc);
|
||||
PrettyStackTraceSILFunction X("silgen emitStoredPropertyInitialization", f);
|
||||
f->createProfiler(init, constant);
|
||||
f->createProfiler(constant);
|
||||
SILGenFunction SGF(*this, *f, initDC);
|
||||
|
||||
// If this is a stored property initializer inside a type at global scope,
|
||||
@@ -981,7 +981,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
|
||||
auto *init = constant.getInitializationExpr();
|
||||
assert(init);
|
||||
|
||||
f->createProfiler(init, constant);
|
||||
f->createProfiler(constant);
|
||||
auto varDC = var->getInnermostDeclContext();
|
||||
SILGenFunction SGF(*this, *f, varDC);
|
||||
SGF.emitGeneratorFunction(constant, init, /*EmitProfilerIncrement*/ true);
|
||||
@@ -1039,7 +1039,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
|
||||
auto *dd = cast<DestructorDecl>(constant.getDecl());
|
||||
preEmitFunction(constant, f, dd);
|
||||
PrettyStackTraceSILFunction X("silgen emitDestroyingDestructor", f);
|
||||
f->createProfiler(dd, constant);
|
||||
f->createProfiler(constant);
|
||||
SILGenFunction(*this, *f, dd).emitDestroyingDestructor(dd);
|
||||
postEmitFunction(constant, f);
|
||||
return;
|
||||
@@ -1053,7 +1053,7 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
|
||||
if (usesObjCAllocator(cd)) {
|
||||
preEmitFunction(constant, f, dd);
|
||||
PrettyStackTraceSILFunction X("silgen emitDestructor -dealloc", f);
|
||||
f->createProfiler(dd, constant);
|
||||
f->createProfiler(constant);
|
||||
SILGenFunction(*this, *f, dd).emitObjCDestructor(constant);
|
||||
postEmitFunction(constant, f);
|
||||
return;
|
||||
@@ -1936,23 +1936,8 @@ void SILGenModule::visitTopLevelCodeDecl(TopLevelCodeDecl *td) {
|
||||
if (!TopLevelSGF->B.hasValidInsertionPoint())
|
||||
return;
|
||||
|
||||
// Retrieve the entry point constant we're emitting for.
|
||||
// FIXME: This won't be necessary once we unify emission of the entry point
|
||||
// such that we walk all of the TopLevelCodeDecls in one shot. This will be
|
||||
// needed in order to requestify entry point emission.
|
||||
auto *SF = td->getParentSourceFile();
|
||||
assert(SF && "TopLevelDecl outside of a SourceFile?");
|
||||
auto entryPoint = TopLevelSGF->F.isAsync()
|
||||
? SILDeclRef::getAsyncMainFileEntryPoint(SF)
|
||||
: SILDeclRef::getMainFileEntryPoint(SF);
|
||||
|
||||
// A single SILFunction may be used to lower multiple top-level decls. When
|
||||
// this happens, fresh profile counters must be assigned to the new decl.
|
||||
TopLevelSGF->F.discardProfiler();
|
||||
TopLevelSGF->F.createProfiler(td, entryPoint);
|
||||
|
||||
TopLevelSGF->emitProfilerIncrement(td->getBody());
|
||||
|
||||
|
||||
DebugScope DS(*TopLevelSGF, CleanupLocation(td));
|
||||
|
||||
for (auto &ESD : td->getBody()->getElements()) {
|
||||
@@ -1985,11 +1970,15 @@ namespace {
|
||||
/// An RAII class to scope source file codegen.
|
||||
class SourceFileScope {
|
||||
SILGenModule &sgm;
|
||||
SILDeclRef EntryRef;
|
||||
Optional<Scope> scope;
|
||||
bool isAsyncTopLevel = false;
|
||||
public:
|
||||
SourceFileScope(SILGenModule &sgm, SourceFile *sf) : sgm(sgm) {
|
||||
// If this is the script-mode file for the module, create a toplevel.
|
||||
// TODO: We need to unify emission of the entry point such that we walk
|
||||
// all of the TopLevelCodeDecls in one shot. This will be needed in order
|
||||
// to requestify entry point emission.
|
||||
if (sf->isScriptMode()) {
|
||||
assert(!sgm.TopLevelSGF && "already emitted toplevel?!");
|
||||
assert(!sgm.M.lookUpFunction(
|
||||
@@ -1999,15 +1988,19 @@ public:
|
||||
auto mainEntryRef = SILDeclRef::getMainFileEntryPoint(sf);
|
||||
SILFunction * toplevel = sgm.getFunction(mainEntryRef, ForDefinition);
|
||||
toplevel->setBare(IsBare);
|
||||
EntryRef = mainEntryRef;
|
||||
|
||||
if (sf->isAsyncContext()) {
|
||||
isAsyncTopLevel = true;
|
||||
auto asyncEntryRef = SILDeclRef::getAsyncMainFileEntryPoint(sf);
|
||||
SILFunction * asyncTopLevel = sgm.getFunction(asyncEntryRef, ForDefinition);
|
||||
auto *asyncTopLevel = sgm.getFunction(asyncEntryRef, ForDefinition);
|
||||
SILGenFunction(sgm, *toplevel, sf).emitAsyncMainThreadStart(asyncEntryRef);
|
||||
toplevel = asyncTopLevel;
|
||||
EntryRef = asyncEntryRef;
|
||||
}
|
||||
|
||||
toplevel->createProfiler(EntryRef);
|
||||
|
||||
sgm.TopLevelSGF = new SILGenFunction(sgm, *toplevel, sf);
|
||||
sgm.TopLevelSGF->MagicFunctionName = sgm.SwiftModule->getName();
|
||||
auto moduleCleanupLoc = CleanupLocation::getModuleCleanupLocation();
|
||||
|
||||
22
test/Profiler/coverage_maindecl.swift
Normal file
22
test/Profiler/coverage_maindecl.swift
Normal file
@@ -0,0 +1,22 @@
|
||||
// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -emit-sil -emit-sorted-sil -module-name coverage_maindecl -parse-as-library %s | %FileCheck %s
|
||||
// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -emit-ir -parse-as-library %s
|
||||
|
||||
// CHECK-NOT: sil_coverage_map {{.*}} "main"
|
||||
|
||||
@main
|
||||
struct S {
|
||||
// CHECK: sil_coverage_map {{.*}} "{{.*}}s17coverage_maindecl1SV4mainyyFZ"
|
||||
// CHECK-NEXT: [[@LINE+1]]:22 -> [[@LINE+10]]:4 : 0
|
||||
static func main() {
|
||||
var i : Int32 = 0
|
||||
|
||||
// CHECK-NEXT: [[@LINE+3]]:11 -> [[@LINE+3]]:19 : (0 + 1)
|
||||
// CHECK-NEXT: [[@LINE+2]]:20 -> [[@LINE+4]]:6 : 1
|
||||
// CHECK-NEXT: [[@LINE+3]]:6 -> [[@LINE+4]]:4 : 0
|
||||
while (i < 10) {
|
||||
i += 1
|
||||
}
|
||||
} // CHECK-NEXT: }
|
||||
}
|
||||
|
||||
// CHECK-NOT: sil_coverage_map {{.*}} "main"
|
||||
@@ -46,6 +46,6 @@ func baz() {
|
||||
baz()
|
||||
|
||||
// IRGEN-LABEL: define {{.*}} @main
|
||||
// IRGEN: call void @llvm.instrprof.increment({{.*}} @"__profn_{{.*}}__tlcd_line
|
||||
// IRGEN: call void @llvm.instrprof.increment({{.*}} @"__profn_{{.*}}main
|
||||
// IRGEN: call void @llvm.instrprof.increment({{.*}} @"__profn_{{.*}}$s18coverage_optimized3bazyyF"
|
||||
// IRGEN: call void @llvm.instrprof.increment({{.*}} @"__profn_{{.*}}$s18coverage_optimized3barSbyF"
|
||||
|
||||
@@ -1,31 +1,39 @@
|
||||
// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -emit-sil -module-name coverage_toplevel %s | %FileCheck %s
|
||||
// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -emit-sil -emit-sorted-sil -module-name coverage_toplevel %s | %FileCheck %s
|
||||
// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -emit-ir %s
|
||||
|
||||
// CHECK: sil_coverage_map{{.*}}__tlcd_line:[[@LINE+2]]:1
|
||||
// CHECK: [[@LINE+1]]:1 -> [[@LINE+1]]:11
|
||||
// CHECK: sil_coverage_map{{.*}}// coverage_toplevel.f1
|
||||
|
||||
// CHECK: sil_coverage_map {{.*}} "main"
|
||||
// CHECK: [[@LINE+1]]:1 -> [[@LINE+1]]:11 : 0
|
||||
print("a")
|
||||
|
||||
// CHECK: sil_coverage_map{{.*}}// coverage_toplevel.f1
|
||||
// Make sure we don't emit regions for these.
|
||||
class C {
|
||||
var k = 0
|
||||
func foo() {}
|
||||
}
|
||||
func f1() {}
|
||||
|
||||
// CHECK-NEXT: [[@LINE+1]]:1 -> [[@LINE+1]]:18 : 1
|
||||
var i : Int32 = 0
|
||||
|
||||
// CHECK: sil_coverage_map{{.*}}__tlcd_line:[[@LINE+2]]:1
|
||||
// CHECK: [[@LINE+1]]:7 -> [[@LINE+1]]:15 : (0 + 1)
|
||||
// CHECK-NEXT: [[@LINE+4]]:1 -> [[@LINE+6]]:2 : 2
|
||||
// CHECK-NEXT: [[@LINE+3]]:7 -> [[@LINE+3]]:15 : (2 + 3)
|
||||
// CHECK-NEXT: [[@LINE+2]]:16 -> [[@LINE+4]]:2 : 3
|
||||
// CHECK-NEXT: [[@LINE+3]]:2 -> [[@LINE+3]]:2 : 2
|
||||
while (i < 10) {
|
||||
i += 1
|
||||
}
|
||||
|
||||
// CHECK: sil_coverage_map{{.*}}__tlcd_line:[[@LINE+3]]:1
|
||||
// CHECK-NEXT: [[@LINE+2]]:17 -> [[@LINE+2]]:18 : 1
|
||||
// CHECK-NEXT: [[@LINE+1]]:21 -> [[@LINE+1]]:22 : (0 - 1)
|
||||
// CHECK-NEXT: [[@LINE+3]]:1 -> [[@LINE+3]]:22 : 4
|
||||
// CHECK-NEXT: [[@LINE+2]]:17 -> [[@LINE+2]]:18 : 5
|
||||
// CHECK-NEXT: [[@LINE+1]]:21 -> [[@LINE+1]]:22 : (4 - 5)
|
||||
var i2 = true ? 1 : 0;
|
||||
|
||||
// CHECK: sil_coverage_map{{.*}}__tlcd_line:[[@LINE+5]]:1
|
||||
// CHECK-NEXT: [[@LINE+4]]:4 -> [[@LINE+4]]:10 : 0
|
||||
// CHECK-NEXT: [[@LINE+3]]:11 -> [[@LINE+5]]:2 : 1
|
||||
// CHECK-NEXT: [[@LINE+2]]:1 -> [[@LINE+4]]:2 : 0
|
||||
// CHECK-NEXT: [[@LINE+3]]:2 -> [[@LINE+3]]:2 : 0
|
||||
// CHECK-NEXT: [[@LINE+4]]:1 -> [[@LINE+6]]:2 : 6
|
||||
// CHECK-NEXT: [[@LINE+3]]:4 -> [[@LINE+3]]:10 : 6
|
||||
// CHECK-NEXT: [[@LINE+2]]:11 -> [[@LINE+4]]:2 : 7
|
||||
// CHECK-NEXT: [[@LINE+3]]:2 -> [[@LINE+3]]:2 : 6
|
||||
if (true) {
|
||||
i2 = 2
|
||||
}
|
||||
|
||||
6
test/Profiler/coverage_toplevel_empty.swift
Normal file
6
test/Profiler/coverage_toplevel_empty.swift
Normal file
@@ -0,0 +1,6 @@
|
||||
// RUN: %target-swift-frontend -Xllvm -sil-full-demangle -profile-generate -profile-coverage-mapping -emit-sil -emit-sorted-sil -module-name coverage_toplevel_empty %s | %FileCheck %s
|
||||
// RUN: %target-swift-frontend -profile-generate -profile-coverage-mapping -emit-ir %s
|
||||
|
||||
// Make sure we don't emit a coverage map for an empty main file with no
|
||||
// top-level code.
|
||||
// CHECK-NOT: sil_coverage_map
|
||||
Reference in New Issue
Block a user