Skip to content

Commit 76c7c3e

Browse files
committed
[opt-remark] Add support for emitting opt-remark-generator remarks when compiling with optimization.
In order to test this, I implemented a small source loc inference routine for instructions without valid SILLocations. This is an optional nob that the opt-remark writer can optionally enable on a per remark basis. The current behaviors are just forward/backward scans in the same basic block. If we scan forwards, if we find a valid SourceLoc, we just use ethat. If we are scanning backwards, instead we grab the SourceRange and if it is valid use the end source range of the given instruction. This seems to give a good result for retain (forward scan) and release (backward scan). The specific reason that I did that is that my test case for this are retain/release operations. Often times these operations due to code-motion are moved around (and rightly to prevent user confusion) given by optimizations auto generated SIL locations. Since that is the test case I am using, to test this I needed said engine.
1 parent d7bd5ae commit 76c7c3e

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)