Skip to content

Commit 6698ef4

Browse files
authored
Merge pull request #74067 from hamishknight/macroscope
[Coverage] Avoid recording regions from macro expansions
2 parents b249eeb + 6c930f7 commit 6698ef4

File tree

3 files changed

+235
-17
lines changed

3 files changed

+235
-17
lines changed

lib/SIL/IR/SILProfiler.cpp

Lines changed: 81 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,23 @@ static bool shouldProfile(SILDeclRef Constant) {
114114
}
115115
}
116116

117-
// Do not profile AST nodes in unavailable contexts.
118117
if (auto *D = DC->getInnermostDeclarationDeclContext()) {
118+
// Do not profile AST nodes in unavailable contexts.
119119
if (D->getSemanticUnavailableAttr()) {
120120
LLVM_DEBUG(llvm::dbgs() << "Skipping ASTNode: unavailable context\n");
121121
return false;
122122
}
123+
124+
// Do not profile functions that have had their bodies replaced (e.g
125+
// function body macros).
126+
// TODO: If/when preamble macros become an official feature, we'll
127+
// need to be more nuanced here.
128+
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
129+
if (AFD->getOriginalBodySourceRange() != AFD->getBodySourceRange()) {
130+
LLVM_DEBUG(llvm::dbgs() << "Skipping function: body replaced\n");
131+
return false;
132+
}
133+
}
123134
}
124135

125136
// Do not profile code that hasn't been written by the user.
@@ -247,9 +258,12 @@ bool shouldSkipExpr(Expr *E) {
247258
/// Whether the children of a decl that isn't explicitly handled should be
248259
/// walked.
249260
static bool shouldWalkIntoUnhandledDecl(const Decl *D) {
250-
// We want to walk into the initializer for a pattern binding decl. This
251-
// allows us to map LazyInitializerExprs.
252-
return isa<PatternBindingDecl>(D);
261+
// We want to walk into initializers for bindings, and the expansions of
262+
// MacroExpansionDecls, which will be nested within MacroExpansionExprs in
263+
// local contexts. We won't record any regions within the macro expansion,
264+
// but still need to walk to get accurate counter information in case e.g
265+
// there's a throwing function call in the expansion.
266+
return isa<PatternBindingDecl>(D) || isa<MacroExpansionDecl>(D);
253267
}
254268

255269
/// Whether the expression \c E could potentially throw an error.
@@ -468,6 +482,10 @@ class SourceMappingRegion {
468482
/// The region's ending location.
469483
std::optional<SourceLoc> EndLoc;
470484

485+
/// Whether the region is within a macro expansion. Such regions do not
486+
/// get recorded, but are needed to track the counters within the expansion.
487+
bool IsInMacroExpansion = false;
488+
471489
SourceMappingRegion(Kind RegionKind, std::optional<CounterExpr> Counter,
472490
std::optional<SourceLoc> StartLoc)
473491
: RegionKind(RegionKind), Counter(Counter), StartLoc(StartLoc) {
@@ -516,6 +534,14 @@ class SourceMappingRegion {
516534
SourceMappingRegion(SourceMappingRegion &&Region) = default;
517535
SourceMappingRegion &operator=(SourceMappingRegion &&RHS) = default;
518536

537+
bool isInMacroExpansion() const {
538+
return IsInMacroExpansion;
539+
}
540+
541+
void setIsInMacroExpansion() {
542+
IsInMacroExpansion = true;
543+
}
544+
519545
/// Whether this region is for scoping only.
520546
bool isForScopingOnly() const { return RegionKind == Kind::ScopingOnly; }
521547

@@ -810,6 +836,7 @@ struct PGOMapping : public ASTWalker {
810836
struct CoverageMapping : public ASTWalker {
811837
private:
812838
const SourceManager &SM;
839+
SourceFile *SF;
813840

814841
/// The SIL function being profiled.
815842
SILDeclRef Constant;
@@ -836,6 +863,12 @@ struct CoverageMapping : public ASTWalker {
836863

837864
Stmt *ImplicitTopLevelBody = nullptr;
838865

866+
/// The number of parent MacroExpansionExprs.
867+
unsigned MacroDepth = 0;
868+
869+
/// Whether the current walk is within a macro expansion.
870+
bool isInMacroExpansion() const { return MacroDepth > 0; }
871+
839872
/// Return true if \c Ref has an associated counter.
840873
bool hasCounter(ProfileCounterRef Ref) { return CounterExprs.count(Ref); }
841874

@@ -992,6 +1025,10 @@ struct CoverageMapping : public ASTWalker {
9921025

9931026
/// Push a region onto the stack.
9941027
void pushRegion(SourceMappingRegion Region) {
1028+
// Note on the region whether we're currently in a macro expansion.
1029+
if (isInMacroExpansion())
1030+
Region.setIsInMacroExpansion();
1031+
9951032
LLVM_DEBUG({
9961033
llvm::dbgs() << "Pushed region: ";
9971034
Region.print(llvm::dbgs(), SM);
@@ -1026,6 +1063,11 @@ struct CoverageMapping : public ASTWalker {
10261063
llvm::dbgs() << "\n";
10271064
});
10281065

1066+
// Don't record regions in macro expansions, they don't have source
1067+
// locations that can be meaningfully mapped to source code.
1068+
if (Region.isInMacroExpansion())
1069+
return;
1070+
10291071
// Don't bother recording regions that are only present for scoping.
10301072
if (Region.isForScopingOnly())
10311073
return;
@@ -1111,9 +1153,10 @@ struct CoverageMapping : public ASTWalker {
11111153

11121154
public:
11131155
CoverageMapping(
1114-
const SourceManager &SM, SILDeclRef Constant,
1156+
SourceFile *SF, SILDeclRef Constant,
11151157
const llvm::DenseMap<ProfileCounterRef, unsigned> &ConcreteCounters)
1116-
: SM(SM), Constant(Constant), ConcreteCounters(ConcreteCounters) {}
1158+
: SM(SF->getASTContext().SourceMgr), SF(SF), Constant(Constant),
1159+
ConcreteCounters(ConcreteCounters) {}
11171160

11181161
LazyInitializerWalking getLazyInitializerWalkingBehavior() override {
11191162
// We want to walk lazy initializers present in the synthesized getter for
@@ -1135,20 +1178,33 @@ struct CoverageMapping : public ASTWalker {
11351178
/// source regions.
11361179
SILCoverageMap *emitSourceRegions(SILModule &M, StringRef Name,
11371180
StringRef PGOFuncName, uint64_t Hash,
1138-
SourceFile *SF, StringRef Filename) {
1181+
StringRef Filename) {
11391182
if (SourceRegions.empty())
11401183
return nullptr;
11411184

1142-
using MappedRegion = SILCoverageMap::MappedRegion;
1185+
auto FileSourceRange = SM.getRangeForBuffer(*SF->getBufferID());
1186+
auto isLocInFile = [&](SourceLoc Loc) {
1187+
return FileSourceRange.contains(Loc) || FileSourceRange.getEnd() == Loc;
1188+
};
11431189

1190+
using MappedRegion = SILCoverageMap::MappedRegion;
11441191
std::vector<MappedRegion> Regions;
11451192
SourceRange OuterRange;
11461193
for (const auto &Region : SourceRegions) {
11471194
assert(Region.hasStartLoc() && "invalid region");
11481195
assert(Region.hasEndLoc() && "incomplete region");
11491196

1150-
// Build up the outer range from the union of all coverage regions.
11511197
SourceRange Range(Region.getStartLoc(), Region.getEndLoc());
1198+
1199+
// Make sure we haven't ended up with any source locations outside the
1200+
// SourceFile (e.g for generated code such as macros), asserting in an
1201+
// asserts build, dropping in a non-asserts build.
1202+
if (!isLocInFile(Range.Start) || !isLocInFile(Range.End)) {
1203+
assert(false && "range outside of file");
1204+
continue;
1205+
}
1206+
1207+
// Build up the outer range from the union of all coverage regions.
11521208
if (!OuterRange) {
11531209
OuterRange = Range;
11541210
} else {
@@ -1520,10 +1576,14 @@ struct CoverageMapping : public ASTWalker {
15201576

15211577
if (hasCounter(E)) {
15221578
pushRegion(SourceMappingRegion::forNode(E, SM));
1523-
} else if (isa<OptionalTryExpr>(E)) {
1579+
} else if (isa<OptionalTryExpr>(E) || isa<MacroExpansionExpr>(E)) {
15241580
// If we have a `try?`, that doesn't already have a counter, record it
15251581
// as a scoping-only region. We need it to scope child error branches,
15261582
// but don't need it in the resulting set of regions.
1583+
//
1584+
// If we have a macro expansion, also push a scoping-only region. We'll
1585+
// discard any regions recorded within the macro, but will adjust for any
1586+
// control flow that may have happened within the macro.
15271587
assignCounter(E, getCurrentCounter());
15281588
pushRegion(SourceMappingRegion::scopingOnly(E, SM));
15291589
}
@@ -1558,6 +1618,10 @@ struct CoverageMapping : public ASTWalker {
15581618
// Already visited the children.
15591619
return Action::SkipChildren(TE);
15601620
}
1621+
1622+
if (isa<MacroExpansionExpr>(E))
1623+
MacroDepth += 1;
1624+
15611625
return shouldWalkIntoExpr(E, Parent, Constant);
15621626
}
15631627

@@ -1574,6 +1638,11 @@ struct CoverageMapping : public ASTWalker {
15741638
Lexer::getLocForEndOfToken(SM, E->getEndLoc()));
15751639
}
15761640

1641+
if (isa<MacroExpansionExpr>(E)) {
1642+
assert(isInMacroExpansion());
1643+
MacroDepth -= 1;
1644+
}
1645+
15771646
if (hasCounter(E))
15781647
exitRegion(E);
15791648

@@ -1610,7 +1679,6 @@ static void walkNode(NodeToProfile Node, ASTWalker &Walker) {
16101679
}
16111680

16121681
void SILProfiler::assignRegionCounters() {
1613-
const auto &SM = M.getASTContext().SourceMgr;
16141682
auto *DC = forDecl.getInnermostDeclContext();
16151683
auto *SF = DC->getParentSourceFile();
16161684
assert(SF && "Not within a SourceFile?");
@@ -1647,10 +1715,10 @@ void SILProfiler::assignRegionCounters() {
16471715
PGOFuncHash = 0x0;
16481716

16491717
if (EmitCoverageMapping) {
1650-
CoverageMapping Coverage(SM, forDecl, RegionCounterMap);
1718+
CoverageMapping Coverage(SF, forDecl, RegionCounterMap);
16511719
walkNode(Root, Coverage);
16521720
CovMap = Coverage.emitSourceRegions(M, CurrentFuncName, PGOFuncName,
1653-
PGOFuncHash, SF, CurrentFileName);
1721+
PGOFuncHash, CurrentFileName);
16541722
}
16551723

16561724
if (llvm::IndexedInstrProfReader *IPR = M.getPGOReader()) {

test/Macros/Inputs/syntax_macro_definitions.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,25 @@ public class NestedDeclInExprMacro: ExpressionMacro {
212212
}
213213
}
214214

215+
public class NullaryFunctionCallMacro: ExpressionMacro {
216+
public static func expansion(
217+
of macro: some FreestandingMacroExpansionSyntax,
218+
in context: some MacroExpansionContext
219+
) -> ExprSyntax {
220+
let calls = macro.arguments.compactMap(\.expression).map { "\($0)()" }
221+
return "(\(raw: calls.joined(separator: ", ")))"
222+
}
223+
}
224+
225+
public class TupleMacro: ExpressionMacro {
226+
public static func expansion(
227+
of macro: some FreestandingMacroExpansionSyntax,
228+
in context: some MacroExpansionContext
229+
) -> ExprSyntax {
230+
return "(\(raw: macro.arguments.map { "\($0)" }.joined()))"
231+
}
232+
}
233+
215234
enum CustomError: Error, CustomStringConvertible {
216235
case message(String)
217236

@@ -2199,6 +2218,15 @@ public struct SingleMemberStubMacro: DeclarationMacro {
21992218
}
22002219
}
22012220

2221+
public struct DeclMacroWithControlFlow: DeclarationMacro {
2222+
public static func expansion(
2223+
of node: some FreestandingMacroExpansionSyntax,
2224+
in context: some MacroExpansionContext
2225+
) throws -> [DeclSyntax] {
2226+
return ["let _ = .random() ? try throwingFn() : 0"]
2227+
}
2228+
}
2229+
22022230
public struct GenerateStubsForProtocolRequirementsMacro: PeerMacro, ExtensionMacro {
22032231
public static func expansion(
22042232
of node: AttributeSyntax,
@@ -2331,6 +2359,20 @@ public struct RemoteBodyMacro: BodyMacro {
23312359
}
23322360
}
23332361

2362+
@_spi(ExperimentalLanguageFeature)
2363+
public struct BodyMacroWithControlFlow: BodyMacro {
2364+
public static func expansion(
2365+
of node: AttributeSyntax,
2366+
providingBodyFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax,
2367+
in context: some MacroExpansionContext
2368+
) throws -> [CodeBlockItemSyntax] {
2369+
[
2370+
"guard .random() else { return }",
2371+
"_ = try throwingFn()"
2372+
]
2373+
}
2374+
}
2375+
23342376
@_spi(ExperimentalLanguageFeature)
23352377
public struct TracedPreambleMacro: PreambleMacro {
23362378
public static func expansion(

0 commit comments

Comments
 (0)