Skip to content

Commit 6298d24

Browse files
authored
Merge pull request #69782 from hamishknight/hash-it-out
2 parents 96ee4eb + 5aab237 commit 6298d24

File tree

12 files changed

+520
-18
lines changed

12 files changed

+520
-18
lines changed

include/swift/AST/SourceFile.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,39 @@ enum class RestrictedImportKind {
4848
/// Import that limits the access level of imported entities.
4949
using ImportAccessLevel = llvm::Optional<AttributedImport<ImportedModule>>;
5050

51+
/// Stores range information for a \c #if block in a SourceFile.
52+
class IfConfigRangeInfo final {
53+
/// The range of the entire \c #if block, including \c #else and \c #endif.
54+
CharSourceRange WholeRange;
55+
56+
/// The range of the active selected body, if there is one. This does not
57+
/// include the outer syntax of the \c #if. This may be invalid, which
58+
/// indicates there is no active body.
59+
CharSourceRange ActiveBodyRange;
60+
61+
public:
62+
IfConfigRangeInfo(CharSourceRange wholeRange, CharSourceRange activeBodyRange)
63+
: WholeRange(wholeRange), ActiveBodyRange(activeBodyRange) {
64+
assert(wholeRange.getByteLength() > 0 && "Range must be non-empty");
65+
assert(activeBodyRange.isInvalid() || wholeRange.contains(activeBodyRange));
66+
}
67+
68+
CharSourceRange getWholeRange() const { return WholeRange; }
69+
SourceLoc getStartLoc() const { return WholeRange.getStart(); }
70+
71+
friend bool operator==(const IfConfigRangeInfo &lhs,
72+
const IfConfigRangeInfo &rhs) {
73+
return lhs.WholeRange == rhs.WholeRange &&
74+
lhs.ActiveBodyRange == rhs.ActiveBodyRange;
75+
}
76+
77+
/// Retrieve the ranges produced by subtracting the active body range from
78+
/// the whole range. This includes both inactive branches as well as the
79+
/// other syntax of the \c #if.
80+
SmallVector<CharSourceRange, 2>
81+
getRangesWithoutActiveBody(const SourceManager &SM) const;
82+
};
83+
5184
/// A file containing Swift source code.
5285
///
5386
/// This is a .swift or .sil file (or a virtual file, such as the contents of
@@ -209,6 +242,19 @@ class SourceFile final : public FileUnit {
209242
ParserStatePtr DelayedParserState =
210243
ParserStatePtr(/*ptr*/ nullptr, /*deleter*/ nullptr);
211244

245+
struct IfConfigRangesData {
246+
/// All the \c #if source ranges in this file.
247+
std::vector<IfConfigRangeInfo> Ranges;
248+
249+
/// Whether the elemnts in \c Ranges are sorted in source order within
250+
/// this file. We flip this to \c false any time a new range gets recorded,
251+
/// and lazily do the sorting when doing a query.
252+
bool IsSorted = false;
253+
};
254+
255+
/// Stores all the \c #if source range info in this file.
256+
mutable IfConfigRangesData IfConfigRanges;
257+
212258
friend class HasImportsMatchingFlagRequest;
213259

214260
/// Indicates which import options have valid caches. Storage for
@@ -448,6 +494,13 @@ class SourceFile final : public FileUnit {
448494
const_cast<SourceFile *>(this)->MissingImportedModules.insert(module);
449495
}
450496

497+
/// Record the source range info for a parsed \c #if block.
498+
void recordIfConfigRangeInfo(IfConfigRangeInfo ranges);
499+
500+
/// Retrieve the source range infos for any \c #if blocks contained within a
501+
/// given source range of this file.
502+
ArrayRef<IfConfigRangeInfo> getIfConfigsWithin(SourceRange outer) const;
503+
451504
void getMissingImportedModules(
452505
SmallVectorImpl<ImportedModule> &imports) const override;
453506

include/swift/SIL/SILCoverageMap.h

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,52 @@ namespace swift {
4141
class SILCoverageMap : public llvm::ilist_node<SILCoverageMap>,
4242
public SILAllocated<SILCoverageMap> {
4343
public:
44-
struct MappedRegion {
44+
class MappedRegion {
45+
public:
46+
enum class Kind {
47+
/// A code region, which represents a regular region of source code.
48+
Code,
49+
50+
/// A skipped region, which represents a region that cannot have any
51+
/// coverage associated with it. This is used for e.g the inactive body of
52+
/// a \c #if.
53+
Skipped
54+
};
55+
56+
Kind RegionKind;
4557
unsigned StartLine;
4658
unsigned StartCol;
4759
unsigned EndLine;
4860
unsigned EndCol;
4961
llvm::coverage::Counter Counter;
5062

51-
MappedRegion(unsigned StartLine, unsigned StartCol, unsigned EndLine,
52-
unsigned EndCol, llvm::coverage::Counter Counter)
53-
: StartLine(StartLine), StartCol(StartCol), EndLine(EndLine),
54-
EndCol(EndCol), Counter(Counter) {}
63+
private:
64+
MappedRegion(Kind RegionKind, unsigned StartLine, unsigned StartCol,
65+
unsigned EndLine, unsigned EndCol,
66+
llvm::coverage::Counter Counter)
67+
: RegionKind(RegionKind), StartLine(StartLine), StartCol(StartCol),
68+
EndLine(EndLine), EndCol(EndCol), Counter(Counter) {}
69+
70+
public:
71+
/// A code region, which represents a regular region of source code.
72+
static MappedRegion code(unsigned StartLine, unsigned StartCol,
73+
unsigned EndLine, unsigned EndCol,
74+
llvm::coverage::Counter Counter) {
75+
return MappedRegion(Kind::Code, StartLine, StartCol, EndLine, EndCol,
76+
Counter);
77+
}
78+
79+
/// A skipped region, which represents a region that cannot have any
80+
/// coverage associated with it. This is used for e.g the inactive body of
81+
/// a \c #if.
82+
static MappedRegion skipped(unsigned StartLine, unsigned StartCol,
83+
unsigned EndLine, unsigned EndCol) {
84+
return MappedRegion(Kind::Skipped, StartLine, StartCol, EndLine, EndCol,
85+
llvm::coverage::Counter());
86+
}
87+
88+
/// Retrieve the equivalent LLVM mapped region.
89+
llvm::coverage::CounterMappingRegion getLLVMRegion(unsigned FileID) const;
5590
};
5691

5792
private:

lib/AST/Module.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3303,6 +3303,70 @@ SourceFile::getImportAccessLevel(const ModuleDecl *targetModule) const {
33033303
return restrictiveImport;
33043304
}
33053305

3306+
SmallVector<CharSourceRange, 2>
3307+
IfConfigRangeInfo::getRangesWithoutActiveBody(const SourceManager &SM) const {
3308+
SmallVector<CharSourceRange, 2> result;
3309+
if (ActiveBodyRange.isValid()) {
3310+
// Split the whole range by the active range.
3311+
result.emplace_back(SM, WholeRange.getStart(), ActiveBodyRange.getStart());
3312+
result.emplace_back(SM, ActiveBodyRange.getEnd(), WholeRange.getEnd());
3313+
} else {
3314+
// No active body, we just return the whole range.
3315+
result.push_back(WholeRange);
3316+
}
3317+
return result;
3318+
}
3319+
3320+
void SourceFile::recordIfConfigRangeInfo(IfConfigRangeInfo ranges) {
3321+
IfConfigRanges.Ranges.push_back(ranges);
3322+
IfConfigRanges.IsSorted = false;
3323+
}
3324+
3325+
ArrayRef<IfConfigRangeInfo>
3326+
SourceFile::getIfConfigsWithin(SourceRange outer) const {
3327+
auto &SM = getASTContext().SourceMgr;
3328+
assert(SM.getRangeForBuffer(BufferID).contains(outer.Start) &&
3329+
"Range not within this file?");
3330+
3331+
if (!IfConfigRanges.IsSorted) {
3332+
// Sort the ranges if we need to.
3333+
llvm::sort(IfConfigRanges.Ranges, [&](IfConfigRangeInfo lhs,
3334+
IfConfigRangeInfo rhs) {
3335+
return SM.isBeforeInBuffer(lhs.getStartLoc(), rhs.getStartLoc());
3336+
});
3337+
3338+
// Be defensive and eliminate duplicates in case we've parsed twice.
3339+
auto newEnd = std::unique(
3340+
IfConfigRanges.Ranges.begin(), IfConfigRanges.Ranges.end(),
3341+
[&](const IfConfigRangeInfo &lhs, const IfConfigRangeInfo &rhs) {
3342+
if (lhs.getWholeRange() != rhs.getWholeRange())
3343+
return false;
3344+
3345+
assert(lhs == rhs && "Active ranges changed on a re-parse?");
3346+
return true;
3347+
});
3348+
IfConfigRanges.Ranges.erase(newEnd, IfConfigRanges.Ranges.end());
3349+
IfConfigRanges.IsSorted = true;
3350+
}
3351+
3352+
// First let's find the first #if that is after the outer start loc.
3353+
auto ranges = llvm::makeArrayRef(IfConfigRanges.Ranges);
3354+
auto lower = llvm::lower_bound(
3355+
ranges, outer.Start, [&](IfConfigRangeInfo range, SourceLoc loc) {
3356+
return SM.isBeforeInBuffer(range.getStartLoc(), loc);
3357+
});
3358+
if (lower == ranges.end() ||
3359+
SM.isBeforeInBuffer(outer.End, lower->getStartLoc())) {
3360+
return {};
3361+
}
3362+
// Next let's find the first #if that's after the outer end loc.
3363+
auto upper = llvm::upper_bound(
3364+
ranges, outer.End, [&](SourceLoc loc, IfConfigRangeInfo range) {
3365+
return SM.isBeforeInBuffer(loc, range.getStartLoc());
3366+
});
3367+
return llvm::makeArrayRef(lower, upper - lower);
3368+
}
3369+
33063370
void ModuleDecl::setPackageName(Identifier name) {
33073371
Package = PackageUnit::create(name, *this, getASTContext());
33083372
}

lib/IRGen/GenCoverage.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,9 @@ void IRGenModule::emitCoverageMaps(ArrayRef<const SILCoverageMap *> Mappings) {
143143

144144
std::vector<CounterMappingRegion> Regions;
145145
for (const auto &MR : M->getMappedRegions()) {
146-
// The SubFileID here is 0, because it's an index into VirtualFileMapping,
146+
// The FileID here is 0, because it's an index into VirtualFileMapping,
147147
// and we only ever have a single file associated for a function.
148-
Regions.emplace_back(CounterMappingRegion::makeRegion(
149-
MR.Counter, /*SubFileID*/ 0, MR.StartLine, MR.StartCol, MR.EndLine,
150-
MR.EndCol));
148+
Regions.push_back(MR.getLLVMRegion(/*FileID*/ 0));
151149
}
152150
// Append each function's regions into the encoded buffer.
153151
ArrayRef<unsigned> VirtualFileMapping(FileID);

lib/Parse/ParseIfConfig.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,7 @@ Result Parser::parseIfConfigRaw(
785785
bool isActive, IfConfigElementsRole role)>
786786
parseElements,
787787
llvm::function_ref<Result(SourceLoc endLoc, bool hadMissingEnd)> finish) {
788+
auto startLoc = Tok.getLoc();
788789
assert(Tok.is(tok::pound_if));
789790

790791
Parser::StructureMarkerRAII ParsingDecl(
@@ -824,6 +825,7 @@ Result Parser::parseIfConfigRaw(
824825

825826
bool foundActive = false;
826827
bool isVersionCondition = false;
828+
CharSourceRange activeBodyRange;
827829
while (1) {
828830
bool isElse = Tok.is(tok::pound_else);
829831
SourceLoc ClauseLoc = consumeToken();
@@ -881,6 +883,7 @@ Result Parser::parseIfConfigRaw(
881883
}
882884

883885
// Parse elements
886+
auto bodyStart = Lexer::getLocForEndOfToken(SourceMgr, PreviousLoc);
884887
llvm::SaveAndRestore<bool> S(InInactiveClauseEnvironment,
885888
InInactiveClauseEnvironment || !isActive);
886889
// Disable updating the interface hash inside inactive blocks.
@@ -899,6 +902,12 @@ Result Parser::parseIfConfigRaw(
899902
ClauseLoc, Condition, isActive, IfConfigElementsRole::Skipped);
900903
}
901904

905+
// Record the active body range for the SourceManager.
906+
if (shouldEvaluate && isActive) {
907+
assert(!activeBodyRange.isValid() && "Multiple active regions?");
908+
activeBodyRange = CharSourceRange(SourceMgr, bodyStart, Tok.getLoc());
909+
}
910+
902911
if (Tok.isNot(tok::pound_elseif, tok::pound_else))
903912
break;
904913

@@ -909,6 +918,12 @@ Result Parser::parseIfConfigRaw(
909918
SourceLoc EndLoc;
910919
bool HadMissingEnd = parseEndIfDirective(EndLoc);
911920

921+
// Record the #if ranges on the SourceManager.
922+
if (!HadMissingEnd && shouldEvaluate) {
923+
auto wholeRange = Lexer::getCharSourceRangeFromSourceRange(
924+
SourceMgr, SourceRange(startLoc, EndLoc));
925+
SF.recordIfConfigRangeInfo({wholeRange, activeBodyRange});
926+
}
912927
return finish(EndLoc, HadMissingEnd);
913928
}
914929

lib/SIL/IR/SILCoverageMap.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
using namespace swift;
2323

2424
using llvm::coverage::CounterExpression;
25+
using llvm::coverage::CounterMappingRegion;
2526

2627
SILCoverageMap *
2728
SILCoverageMap::create(SILModule &M, SourceFile *ParentSourceFile,
@@ -56,6 +57,19 @@ SILCoverageMap::SILCoverageMap(SourceFile *ParentSourceFile, uint64_t Hash)
5657

5758
SILCoverageMap::~SILCoverageMap() {}
5859

60+
CounterMappingRegion
61+
SILCoverageMap::MappedRegion::getLLVMRegion(unsigned int FileID) const {
62+
switch (RegionKind) {
63+
case MappedRegion::Kind::Code:
64+
return CounterMappingRegion::makeRegion(Counter, FileID, StartLine,
65+
StartCol, EndLine, EndCol);
66+
case MappedRegion::Kind::Skipped:
67+
return CounterMappingRegion::makeSkipped(FileID, StartLine, StartCol,
68+
EndLine, EndCol);
69+
}
70+
llvm_unreachable("Unhandled case in switch!");
71+
}
72+
5973
namespace {
6074
struct Printer {
6175
const llvm::coverage::Counter &C;

lib/SIL/IR/SILPrinter.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4168,7 +4168,14 @@ void SILCoverageMap::print(SILPrintContext &PrintCtx) const {
41684168
for (auto &MR : getMappedRegions()) {
41694169
OS << " " << MR.StartLine << ":" << MR.StartCol << " -> " << MR.EndLine
41704170
<< ":" << MR.EndCol << " : ";
4171-
printCounter(OS, MR.Counter);
4171+
switch (MR.RegionKind) {
4172+
case MappedRegion::Kind::Code:
4173+
printCounter(OS, MR.Counter);
4174+
break;
4175+
case MappedRegion::Kind::Skipped:
4176+
OS << "skipped";
4177+
break;
4178+
}
41724179
OS << "\n";
41734180
}
41744181
OS << "}\n\n";

lib/SIL/IR/SILProfiler.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,19 +1066,41 @@ struct CoverageMapping : public ASTWalker {
10661066
if (SourceRegions.empty())
10671067
return nullptr;
10681068

1069+
using MappedRegion = SILCoverageMap::MappedRegion;
1070+
10691071
llvm::coverage::CounterExpressionBuilder Builder;
1070-
std::vector<SILCoverageMap::MappedRegion> Regions;
1072+
std::vector<MappedRegion> Regions;
1073+
SourceRange OuterRange;
10711074
for (const auto &Region : SourceRegions) {
10721075
assert(Region.hasStartLoc() && "invalid region");
10731076
assert(Region.hasEndLoc() && "incomplete region");
10741077

1078+
// Build up the outer range from the union of all coverage regions.
1079+
SourceRange Range(Region.getStartLoc(), Region.getEndLoc());
1080+
if (!OuterRange) {
1081+
OuterRange = Range;
1082+
} else {
1083+
OuterRange.widen(Range);
1084+
}
1085+
10751086
auto Start = SM.getLineAndColumnInBuffer(Region.getStartLoc());
10761087
auto End = SM.getLineAndColumnInBuffer(Region.getEndLoc());
10771088
assert(Start.first <= End.first && "region start and end out of order");
10781089

10791090
auto Counter = Region.getCounter(CounterMap);
1080-
Regions.emplace_back(Start.first, Start.second, End.first, End.second,
1081-
Counter.expand(Builder, CounterIndices));
1091+
Regions.push_back(
1092+
MappedRegion::code(Start.first, Start.second, End.first, End.second,
1093+
Counter.expand(Builder, CounterIndices)));
1094+
}
1095+
// Add any skipped regions present in the outer range.
1096+
for (auto IfConfig : SF->getIfConfigsWithin(OuterRange)) {
1097+
for (auto SkipRange : IfConfig.getRangesWithoutActiveBody(SM)) {
1098+
auto Start = SM.getLineAndColumnInBuffer(SkipRange.getStart());
1099+
auto End = SM.getLineAndColumnInBuffer(SkipRange.getEnd());
1100+
assert(Start.first <= End.first && "region start and end out of order");
1101+
Regions.push_back(MappedRegion::skipped(Start.first, Start.second,
1102+
End.first, End.second));
1103+
}
10821104
}
10831105
return SILCoverageMap::create(M, SF, Filename, Name, PGOFuncName, Hash,
10841106
Regions, Builder.getExpressions());

lib/SIL/Parser/ParseSIL.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8621,13 +8621,22 @@ bool SILParserState::parseSILCoverageMap(Parser &P) {
86218621
break;
86228622
}
86238623

8624-
auto Counter = State.parseSILCoverageExpr(Builder);
8625-
if (!Counter) {
8626-
BodyHasError = true;
8627-
break;
8624+
using MappedRegion = SILCoverageMap::MappedRegion;
8625+
8626+
if (P.Tok.isContextualKeyword("skipped")) {
8627+
P.consumeToken();
8628+
Regions.push_back(
8629+
MappedRegion::skipped(StartLine, StartCol, EndLine, EndCol));
8630+
} else {
8631+
auto Counter = State.parseSILCoverageExpr(Builder);
8632+
if (!Counter) {
8633+
BodyHasError = true;
8634+
break;
8635+
}
8636+
Regions.push_back(
8637+
MappedRegion::code(StartLine, StartCol, EndLine, EndCol, *Counter));
86288638
}
86298639

8630-
Regions.emplace_back(StartLine, StartCol, EndLine, EndCol, *Counter);
86318640
} while (P.Tok.isNot(tok::r_brace) && P.Tok.isNot(tok::eof));
86328641
}
86338642
if (BodyHasError)

0 commit comments

Comments
 (0)