Skip to content

Commit 7a5f525

Browse files
authored
Merge pull request #32986 from gottesmm/pr-967e8caef81ac33bc24c69828271796b964db211
[opt-remark] Add support for emitting opt-remark-generator remarks when compiling with optimization.
2 parents 768a404 + 76c7c3e commit 7a5f525

File tree

6 files changed

+179
-22
lines changed

6 files changed

+179
-22
lines changed

include/swift/SIL/OptimizationRemark.h

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,29 @@ struct IndentDebug {
6565
unsigned width;
6666
};
6767

68+
enum class SourceLocInferenceBehavior {
69+
None,
70+
ForwardScanOnly,
71+
BackwardScanOnly,
72+
ForwardThenBackward,
73+
BackwardThenForward,
74+
};
75+
76+
/// Infer the proper SourceLoc to use for the given SILInstruction.
77+
///
78+
/// This means that if we have a valid location for the instruction, we just
79+
/// return that. Otherwise, we have a runtime instruction that does not have a
80+
/// valid source location. In such a case, we infer the source location from the
81+
/// surrounding code. If we can not find any surrounding code, we return an
82+
/// invalid SourceLoc.
83+
SourceLoc inferOptRemarkSourceLoc(SILInstruction &i,
84+
SourceLocInferenceBehavior inferBehavior);
85+
6886
/// The base class for remarks. This can be created by optimization passed to
6987
/// report successful and unsuccessful optimizations. CRTP is used to preserve
7088
/// the underlying type encoding the remark kind in the insertion operator.
71-
template <typename DerivedT> class Remark {
89+
template <typename DerivedT>
90+
class Remark {
7291
/// Arguments collected via the streaming interface.
7392
SmallVector<Argument, 4> args;
7493

@@ -93,9 +112,10 @@ template <typename DerivedT> class Remark {
93112
unsigned indentDebugWidth = 0;
94113

95114
protected:
96-
Remark(StringRef identifier, SILInstruction &i)
115+
Remark(StringRef identifier, SILInstruction &i,
116+
SourceLocInferenceBehavior inferenceBehavior)
97117
: identifier((Twine("sil.") + identifier).str()),
98-
location(i.getLoc().getSourceLoc()),
118+
location(inferOptRemarkSourceLoc(i, inferenceBehavior)),
99119
function(i.getParent()->getParent()),
100120
demangledFunctionName(Demangle::demangleSymbolAsString(
101121
function->getName(),
@@ -133,11 +153,19 @@ template <typename DerivedT> class Remark {
133153

134154
/// Remark to report a successful optimization.
135155
struct RemarkPassed : public Remark<RemarkPassed> {
136-
RemarkPassed(StringRef id, SILInstruction &i) : Remark(id, i) {}
156+
RemarkPassed(StringRef id, SILInstruction &i)
157+
: Remark(id, i, SourceLocInferenceBehavior::None) {}
158+
RemarkPassed(StringRef id, SILInstruction &i,
159+
SourceLocInferenceBehavior inferenceBehavior)
160+
: Remark(id, i, inferenceBehavior) {}
137161
};
138162
/// Remark to report a unsuccessful optimization.
139163
struct RemarkMissed : public Remark<RemarkMissed> {
140-
RemarkMissed(StringRef id, SILInstruction &i) : Remark(id, i) {}
164+
RemarkMissed(StringRef id, SILInstruction &i)
165+
: Remark(id, i, SourceLocInferenceBehavior::None) {}
166+
RemarkMissed(StringRef id, SILInstruction &i,
167+
SourceLocInferenceBehavior inferenceBehavior)
168+
: Remark(id, i, inferenceBehavior) {}
141169
};
142170

143171
/// Used to emit the remarks. Passes reporting remarks should create an

lib/SIL/Utils/OptimizationRemark.cpp

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/Demangling/Demangler.h"
2323
#include "swift/SIL/SILRemarkStreamer.h"
2424
#include "llvm/ADT/StringExtras.h"
25+
#include "llvm/Support/CommandLine.h"
2526
#include "llvm/Support/raw_ostream.h"
2627

2728
using namespace swift;
@@ -67,15 +68,17 @@ Argument::Argument(StringRef key, CanType ty) : key(key) {
6768
ty.print(stream);
6869
}
6970

70-
template <typename DerivedT> std::string Remark<DerivedT>::getMsg() const {
71+
template <typename DerivedT>
72+
std::string Remark<DerivedT>::getMsg() const {
7173
std::string str;
7274
llvm::raw_string_ostream stream(str);
7375
for (const Argument &arg : args)
7476
stream << arg.val;
7577
return stream.str();
7678
}
7779

78-
template <typename DerivedT> std::string Remark<DerivedT>::getDebugMsg() const {
80+
template <typename DerivedT>
81+
std::string Remark<DerivedT>::getDebugMsg() const {
7982
std::string str;
8083
llvm::raw_string_ostream stream(str);
8184

@@ -100,6 +103,90 @@ Emitter::Emitter(StringRef passName, SILModule &m)
100103
m.getASTContext().LangOpts.OptimizationRemarkMissedPattern->match(
101104
passName)) {}
102105

106+
/// The user has passed us an instruction that for some reason has a source loc
107+
/// that can not be used. Search down the current block for an instruction with
108+
/// a valid source loc and use that instead.
109+
static SourceLoc inferOptRemarkSearchForwards(SILInstruction &i) {
110+
for (auto &inst :
111+
llvm::make_range(std::next(i.getIterator()), i.getParent()->end())) {
112+
auto newLoc = inst.getLoc().getSourceLoc();
113+
if (newLoc.isValid())
114+
return newLoc;
115+
}
116+
117+
return SourceLoc();
118+
}
119+
120+
/// The user has passed us an instruction that for some reason has a source loc
121+
/// that can not be used. Search up the current block for an instruction with
122+
/// a valid SILLocation and use the end SourceLoc of the SourceRange for the
123+
/// instruction.
124+
static SourceLoc inferOptRemarkSearchBackwards(SILInstruction &i) {
125+
for (auto &inst : llvm::make_range(std::next(i.getReverseIterator()),
126+
i.getParent()->rend())) {
127+
auto loc = inst.getLoc();
128+
if (!bool(loc))
129+
continue;
130+
131+
auto range = loc.getSourceRange();
132+
if (range.isValid())
133+
return range.End;
134+
}
135+
136+
return SourceLoc();
137+
}
138+
139+
SourceLoc swift::OptRemark::inferOptRemarkSourceLoc(
140+
SILInstruction &i, SourceLocInferenceBehavior inferBehavior) {
141+
auto loc = i.getLoc().getSourceLoc();
142+
143+
// Do a quick check if we already have a valid loc. In such a case, just
144+
// return. Otherwise, we try to infer using one of our heuristics below.
145+
if (loc.isValid())
146+
return loc;
147+
148+
// Otherwise, try to handle the individual behavior cases, returning loc at
149+
// the end of each case (its invalid, so it will get ignored). If loc is not
150+
// returned, we hit an assert at the end to make it easy to identify a case
151+
// was missed.
152+
switch (inferBehavior) {
153+
case SourceLocInferenceBehavior::None:
154+
return loc;
155+
case SourceLocInferenceBehavior::ForwardScanOnly: {
156+
SourceLoc newLoc = inferOptRemarkSearchForwards(i);
157+
if (newLoc.isValid())
158+
return newLoc;
159+
return loc;
160+
}
161+
case SourceLocInferenceBehavior::BackwardScanOnly: {
162+
SourceLoc newLoc = inferOptRemarkSearchBackwards(i);
163+
if (newLoc.isValid())
164+
return newLoc;
165+
return loc;
166+
}
167+
case SourceLocInferenceBehavior::ForwardThenBackward: {
168+
SourceLoc newLoc = inferOptRemarkSearchForwards(i);
169+
if (newLoc.isValid())
170+
return newLoc;
171+
newLoc = inferOptRemarkSearchBackwards(i);
172+
if (newLoc.isValid())
173+
return newLoc;
174+
return loc;
175+
}
176+
case SourceLocInferenceBehavior::BackwardThenForward: {
177+
SourceLoc newLoc = inferOptRemarkSearchBackwards(i);
178+
if (newLoc.isValid())
179+
return newLoc;
180+
newLoc = inferOptRemarkSearchForwards(i);
181+
if (newLoc.isValid())
182+
return newLoc;
183+
return loc;
184+
}
185+
}
186+
187+
llvm_unreachable("Covered switch isn't covered?!");
188+
}
189+
103190
template <typename RemarkT, typename... ArgTypes>
104191
static void emitRemark(SILModule &module, const Remark<RemarkT> &remark,
105192
Diag<ArgTypes...> id, bool diagEnabled) {

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,9 @@ static void addLastChanceOptPassPipeline(SILPassPipelinePlan &P) {
660660

661661
// Only has an effect if the -assume-single-thread option is specified.
662662
P.addAssumeSingleThreaded();
663+
664+
// Only has an effect if opt-remark is enabled.
665+
P.addOptRemarkGenerator();
663666
}
664667

665668
static void addSILDebugInfoGeneratorPipeline(SILPassPipelinePlan &P) {

lib/SILOptimizer/Transforms/OptRemarkGenerator.cpp

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,35 +49,43 @@ void OptRemarkGeneratorInstructionVisitor::visitStrongRetainInst(
4949
StrongRetainInst *sri) {
5050
ORE.emit([&]() {
5151
using namespace OptRemark;
52-
return RemarkMissed("memory-management", *sri)
53-
<< "Unable to remove ARC operation";
52+
// Retains begin a lifetime scope so we infer scan forward.
53+
return RemarkMissed("memory-management", *sri,
54+
SourceLocInferenceBehavior::ForwardScanOnly)
55+
<< "Unable to remove retain";
5456
});
5557
}
5658

5759
void OptRemarkGeneratorInstructionVisitor::visitStrongReleaseInst(
5860
StrongReleaseInst *sri) {
5961
ORE.emit([&]() {
6062
using namespace OptRemark;
61-
return RemarkMissed("memory-management", *sri)
62-
<< "Unable to remove ARC operation";
63+
// Releases end a lifetime scope so we infer scan backward.
64+
return RemarkMissed("memory-management", *sri,
65+
SourceLocInferenceBehavior::BackwardScanOnly)
66+
<< "Unable to remove release";
6367
});
6468
}
6569

6670
void OptRemarkGeneratorInstructionVisitor::visitRetainValueInst(
6771
RetainValueInst *rvi) {
6872
ORE.emit([&]() {
6973
using namespace OptRemark;
70-
return RemarkMissed("memory-management", *rvi)
71-
<< "Unable to remove ARC operation";
74+
// Retains begin a lifetime scope, so we infer scan forwards.
75+
return RemarkMissed("memory-management", *rvi,
76+
SourceLocInferenceBehavior::ForwardScanOnly)
77+
<< "Unable to remove retain";
7278
});
7379
}
7480

7581
void OptRemarkGeneratorInstructionVisitor::visitReleaseValueInst(
7682
ReleaseValueInst *rvi) {
7783
ORE.emit([&]() {
7884
using namespace OptRemark;
79-
return RemarkMissed("memory-management", *rvi)
80-
<< "Unable to remove ARC operation";
85+
// Releases end a lifetime scope so we infer scan backward.
86+
return RemarkMissed("memory-management", *rvi,
87+
SourceLocInferenceBehavior::BackwardScanOnly)
88+
<< "Unable to remove release";
8189
});
8290
}
8391

@@ -90,10 +98,22 @@ namespace {
9098
class OptRemarkGenerator : public SILFunctionTransform {
9199
~OptRemarkGenerator() override {}
92100

101+
bool isOptRemarksEnabled() {
102+
// TODO: Put this on LangOpts as a helper.
103+
auto &langOpts = getFunction()->getASTContext().LangOpts;
104+
return bool(langOpts.OptimizationRemarkMissedPattern) ||
105+
bool(langOpts.OptimizationRemarkPassedPattern);
106+
}
107+
93108
/// The entry point to the transformation.
94109
void run() override {
95-
OptRemarkGeneratorInstructionVisitor visitor(getFunction()->getModule());
96-
for (auto &block : *getFunction()) {
110+
if (!isOptRemarksEnabled())
111+
return;
112+
113+
auto *fn = getFunction();
114+
LLVM_DEBUG(llvm::dbgs() << "Visiting: " << fn->getName() << "\n");
115+
OptRemarkGeneratorInstructionVisitor visitor(fn->getModule());
116+
for (auto &block : *fn) {
97117
for (auto &inst : block) {
98118
visitor.visit(&inst);
99119
}

test/SILOptimizer/opt-remark-generator.sil

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
// RUN: %target-sil-opt -sil-opt-remark-generator -sil-remarks-missed=sil-opt-remark-gen %s -o /dev/null 2>&1 | %FileCheck %s
22

3-
// CHECK: {{.*}}opt-remark-generator.sil:18:3: remark: Unable to remove ARC operation
3+
// CHECK: {{.*}}opt-remark-generator.sil:18:3: remark: Unable to remove retain
44
// CHECK-NEXT: strong_retain
5-
// CHECK: {{.*}}opt-remark-generator.sil:19:3: remark: Unable to remove ARC operation
5+
// CHECK: {{.*}}opt-remark-generator.sil:19:3: remark: Unable to remove retain
66
// CHECK-NEXT: retain_value
7-
// CHECK: {{.*}}opt-remark-generator.sil:20:3: remark: Unable to remove ARC operation
7+
// CHECK: {{.*}}opt-remark-generator.sil:20:3: remark: Unable to remove release
88
// CHECK-NEXT: strong_release
9-
// CHECK: {{.*}}opt-remark-generator.sil:21:3: remark: Unable to remove ARC operation
9+
// CHECK: {{.*}}opt-remark-generator.sil:21:3: remark: Unable to remove release
1010
// CHECK-NEXT: release_value
1111

1212
sil_stage canonical
@@ -21,4 +21,4 @@ bb0(%0 : $Builtin.NativeObject):
2121
release_value %0 : $Builtin.NativeObject
2222
%9999 = tuple()
2323
return %9999 : $()
24-
}
24+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: %target-swiftc_driver -O -Rpass-missed=sil-opt-remark-gen -Xllvm -sil-disable-pass=FunctionSignatureOpts -emit-sil %s -o /dev/null -Xfrontend -verify
2+
3+
public class Klass {}
4+
5+
public var global = Klass()
6+
7+
@inline(never)
8+
public func getGlobal() -> Klass {
9+
return global // expected-remark @:5 {{Unable to remove retain}}
10+
}
11+
12+
public func useGlobal() {
13+
let x = getGlobal()
14+
// Make sure that the retain msg is at the beginning of the print and the
15+
// releases are the end of the print.
16+
print(x) // expected-remark @:5 {{Unable to remove retain}}
17+
// expected-remark @-1:12 {{Unable to remove release}}
18+
// expected-remark @-2:12 {{Unable to remove release}}
19+
}

0 commit comments

Comments
 (0)