Skip to content

Commit 0c7894f

Browse files
authored
Merge pull request #33126 from gottesmm/pr-0798e887d05d3200b9055617e87b3f37f6f81f21
[opt-remark] Add @_semantics("optremark{.$SIL_PASS_NAME}") that force opt remarks on functions.
2 parents d1ae693 + 96097b0 commit 0c7894f

File tree

9 files changed

+252
-113
lines changed

9 files changed

+252
-113
lines changed

include/swift/AST/SemanticAttrs.def

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,5 +93,16 @@ SEMANTICS_ATTR(CONVERT_TO_OBJECTIVE_C, "convertToObjectiveC")
9393

9494
SEMANTICS_ATTR(KEYPATH_KVC_KEY_PATH_STRING, "keypath.kvcKeyPathString")
9595

96+
/// The prefix used to force opt-remarks to be emitted in a specific function.
97+
///
98+
/// If used just by itself "optremark", it is assumed that /all/ opt remarks
99+
/// should be emitted. Otherwise, one can add a suffix after a '.' that
100+
/// specifies a pass to emit opt-remarks from. So for instance to get just
101+
/// information from 'sil-opt-remark-gen', one would write:
102+
/// "optremark.sil-opt-remark-gen". One can add as many as one wishes. Keep in
103+
/// mind that if the function itself is inlined, one will lose the optremark so
104+
/// consider inlining where to put these.
105+
SEMANTICS_ATTR(FORCE_EMIT_OPT_REMARK_PREFIX, "optremark")
106+
96107
#undef SEMANTICS_ATTR
97108

include/swift/SIL/OptimizationRemark.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#ifndef SWIFT_SIL_OPTIMIZATIONREMARKEMITTER_H
2020
#define SWIFT_SIL_OPTIMIZATIONREMARKEMITTER_H
2121

22+
#include "swift/AST/SemanticAttrs.h"
2223
#include "swift/Basic/SourceLoc.h"
2324
#include "swift/Demangling/Demangler.h"
2425
#include "swift/SIL/SILArgument.h"

lib/SIL/Utils/OptimizationRemark.cpp

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@
1919
#include "swift/SIL/OptimizationRemark.h"
2020
#include "swift/AST/DiagnosticEngine.h"
2121
#include "swift/AST/DiagnosticsSIL.h"
22+
#include "swift/AST/SemanticAttrs.h"
2223
#include "swift/Demangling/Demangler.h"
2324
#include "swift/SIL/DebugUtils.h"
2425
#include "swift/SIL/InstructionUtils.h"
2526
#include "swift/SIL/MemAccessUtils.h"
27+
#include "swift/SIL/Projection.h"
2628
#include "swift/SIL/SILArgument.h"
2729
#include "swift/SIL/SILInstruction.h"
2830
#include "swift/SIL/SILRemarkStreamer.h"
@@ -108,16 +110,39 @@ std::string Remark<DerivedT>::getDebugMsg() const {
108110
return stream.str();
109111
}
110112

113+
static bool hasForceEmitSemanticAttr(SILFunction &fn, StringRef passName) {
114+
return llvm::any_of(fn.getSemanticsAttrs(), [&](const std::string &str) {
115+
auto ref = StringRef(str);
116+
117+
// First try to chomp the prefix.
118+
if (!ref.consume_front(semantics::FORCE_EMIT_OPT_REMARK_PREFIX))
119+
return false;
120+
121+
// Then see if we only have the prefix. Then always return true the user
122+
// wants /all/ remarks.
123+
if (ref.empty())
124+
return true;
125+
126+
// Otherwise, lets try to chomp the '.' and then the passName.
127+
if (!ref.consume_front(".") || !ref.consume_front(passName))
128+
return false;
129+
130+
return ref.empty();
131+
});
132+
}
133+
111134
Emitter::Emitter(StringRef passName, SILFunction &fn)
112135
: fn(fn), passName(passName),
113136
passedEnabled(
114-
fn.getASTContext().LangOpts.OptimizationRemarkPassedPattern &&
115-
fn.getASTContext().LangOpts.OptimizationRemarkPassedPattern->match(
116-
passName)),
137+
hasForceEmitSemanticAttr(fn, passName) ||
138+
(fn.getASTContext().LangOpts.OptimizationRemarkPassedPattern &&
139+
fn.getASTContext().LangOpts.OptimizationRemarkPassedPattern->match(
140+
passName))),
117141
missedEnabled(
118-
fn.getASTContext().LangOpts.OptimizationRemarkMissedPattern &&
119-
fn.getASTContext().LangOpts.OptimizationRemarkMissedPattern->match(
120-
passName)) {}
142+
hasForceEmitSemanticAttr(fn, passName) ||
143+
(fn.getASTContext().LangOpts.OptimizationRemarkMissedPattern &&
144+
fn.getASTContext().LangOpts.OptimizationRemarkMissedPattern->match(
145+
passName))) {}
121146

122147
/// The user has passed us an instruction that for some reason has a source loc
123148
/// that can not be used. Search down the current block for an instruction with
@@ -216,7 +241,8 @@ static void emitRemark(SILFunction &fn, const Remark<RemarkT> &remark,
216241
// If diagnostics are enabled, first emit the main diagnostic and then loop
217242
// through our arguments and allow the arguments to add additional diagnostics
218243
// if they want.
219-
if (!diagEnabled)
244+
if (!diagEnabled && !fn.hasSemanticsAttrThatStartsWith(
245+
semantics::FORCE_EMIT_OPT_REMARK_PREFIX))
220246
return;
221247

222248
auto &de = module.getASTContext().Diags;

lib/SILOptimizer/Transforms/OptRemarkGenerator.cpp

Lines changed: 76 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,18 @@
1212

1313
#define DEBUG_TYPE "sil-opt-remark-gen"
1414

15+
#include "swift/AST/SemanticAttrs.h"
1516
#include "swift/SIL/MemAccessUtils.h"
1617
#include "swift/SIL/OptimizationRemark.h"
18+
#include "swift/SIL/Projection.h"
1719
#include "swift/SIL/SILFunction.h"
1820
#include "swift/SIL/SILInstruction.h"
1921
#include "swift/SIL/SILModule.h"
2022
#include "swift/SIL/SILVisitor.h"
2123
#include "swift/SILOptimizer/Analysis/RCIdentityAnalysis.h"
2224
#include "swift/SILOptimizer/PassManager/Passes.h"
2325
#include "swift/SILOptimizer/PassManager/Transforms.h"
26+
#include "llvm/Support/raw_ostream.h"
2427

2528
using namespace swift;
2629

@@ -42,14 +45,18 @@ struct ValueToDeclInferrer {
4245
using Argument = OptRemark::Argument;
4346
using ArgumentKeyKind = OptRemark::ArgumentKeyKind;
4447

45-
bool infer(ArgumentKeyKind keyKind, StringRef msg, SILValue value,
48+
bool infer(ArgumentKeyKind keyKind, SILValue value,
4649
function_ref<bool(Argument)> funcPassedInferedArgs);
4750
};
4851

4952
} // anonymous namespace
5053

54+
static void printNote(llvm::raw_string_ostream &stream, const ValueDecl *decl) {
55+
stream << "of '" << decl->getBaseName() << "'";
56+
}
57+
5158
bool ValueToDeclInferrer::infer(
52-
ArgumentKeyKind keyKind, StringRef msg, SILValue value,
59+
ArgumentKeyKind keyKind, SILValue value,
5360
function_ref<bool(Argument)> funcPassedInferedArgs) {
5461

5562
// This is a linear IR traversal using a 'falling while loop'. That means
@@ -60,15 +67,27 @@ bool ValueToDeclInferrer::infer(
6067
while (true) {
6168
// First check for "identified values" like arguments and global_addr.
6269
if (auto *arg = dyn_cast<SILArgument>(value))
63-
if (auto *decl = arg->getDecl())
70+
if (auto *decl = arg->getDecl()) {
71+
std::string msg;
72+
{
73+
llvm::raw_string_ostream stream(msg);
74+
printNote(stream, decl);
75+
}
6476
return funcPassedInferedArgs(
65-
Argument({keyKind, "InferredValue"}, msg, decl));
77+
Argument({keyKind, "InferredValue"}, std::move(msg), decl));
78+
}
6679

6780
if (auto *ga = dyn_cast<GlobalAddrInst>(value))
68-
if (auto *decl = ga->getReferencedGlobal()->getDecl())
81+
if (auto *decl = ga->getReferencedGlobal()->getDecl()) {
82+
std::string msg;
83+
{
84+
llvm::raw_string_ostream stream(msg);
85+
printNote(stream, decl);
86+
}
6987
if (!funcPassedInferedArgs(
70-
Argument({keyKind, "InferredValue"}, msg, decl)))
88+
Argument({keyKind, "InferredValue"}, std::move(msg), decl)))
7189
return false;
90+
}
7291

7392
// Then visit our users and see if we can find a debug_value that provides
7493
// us with a decl we can use to construct an argument.
@@ -78,15 +97,23 @@ bool ValueToDeclInferrer::infer(
7897
continue;
7998

8099
if (auto *dvi = dyn_cast<DebugValueInst>(use->getUser())) {
81-
if (auto *decl = dvi->getDecl())
100+
if (auto *decl = dvi->getDecl()) {
101+
std::string msg;
102+
{
103+
llvm::raw_string_ostream stream(msg);
104+
printNote(stream, decl);
105+
}
82106
if (!funcPassedInferedArgs(
83-
Argument({keyKind, "InferredValue"}, msg, decl)))
107+
Argument({keyKind, "InferredValue"}, std::move(msg), decl)))
84108
return false;
109+
}
85110
}
86111
}
87112

88113
// At this point, we could not infer any argument. See if we can look
89-
// through loads, geps to construct a ProjectionPath.
114+
// through loads.
115+
//
116+
// TODO: Add GEPs to construct a ProjectionPath.
90117

91118
// Finally, see if we can look through a load...
92119
if (auto *li = dyn_cast<LoadInst>(value)) {
@@ -135,24 +162,19 @@ void OptRemarkGeneratorInstructionVisitor::visitStrongRetainInst(
135162
using namespace OptRemark;
136163
SILValue root = rcfi.getRCIdentityRoot(sri->getOperand());
137164
SmallVector<Argument, 8> inferredArgs;
138-
bool foundArgs = valueToDeclInferrer.infer(
139-
ArgumentKeyKind::Note, "on value:", root, [&](Argument arg) {
140-
inferredArgs.push_back(arg);
141-
return true;
142-
});
165+
bool foundArgs = valueToDeclInferrer.infer(ArgumentKeyKind::Note, root,
166+
[&](Argument arg) {
167+
inferredArgs.push_back(arg);
168+
return true;
169+
});
143170
(void)foundArgs;
144171

145172
// Retains begin a lifetime scope so we infer scan forward.
146-
auto remark = RemarkMissed("memory-management", *sri,
173+
auto remark = RemarkMissed("memory", *sri,
147174
SourceLocInferenceBehavior::ForwardScanOnly)
148-
<< "Found retain:";
149-
if (inferredArgs.empty()) {
150-
remark << Argument({ArgumentKeyKind::ParentLocNote, "InferValueFailure"},
151-
"Unable to infer any values being retained.");
152-
} else {
153-
for (auto arg : inferredArgs) {
154-
remark << arg;
155-
}
175+
<< "retain";
176+
for (auto arg : inferredArgs) {
177+
remark << arg;
156178
}
157179
return remark;
158180
});
@@ -165,22 +187,18 @@ void OptRemarkGeneratorInstructionVisitor::visitStrongReleaseInst(
165187
// Releases end a lifetime scope so we infer scan backward.
166188
SILValue root = rcfi.getRCIdentityRoot(sri->getOperand());
167189
SmallVector<Argument, 8> inferredArgs;
168-
bool foundArgs = valueToDeclInferrer.infer(
169-
ArgumentKeyKind::Note, "on value:", root, [&](Argument arg) {
170-
inferredArgs.push_back(arg);
171-
return true;
172-
});
190+
bool foundArgs = valueToDeclInferrer.infer(ArgumentKeyKind::Note, root,
191+
[&](Argument arg) {
192+
inferredArgs.push_back(arg);
193+
return true;
194+
});
173195
(void)foundArgs;
174-
auto remark = RemarkMissed("memory-management", *sri,
196+
197+
auto remark = RemarkMissed("memory", *sri,
175198
SourceLocInferenceBehavior::BackwardScanOnly)
176-
<< "Found release:";
177-
if (inferredArgs.empty()) {
178-
remark << Argument({ArgumentKeyKind::ParentLocNote, "InferValueFailure"},
179-
"Unable to infer any values being released.");
180-
} else {
181-
for (auto arg : inferredArgs) {
182-
remark << arg;
183-
}
199+
<< "release";
200+
for (auto arg : inferredArgs) {
201+
remark << arg;
184202
}
185203
return remark;
186204
});
@@ -192,24 +210,19 @@ void OptRemarkGeneratorInstructionVisitor::visitRetainValueInst(
192210
using namespace OptRemark;
193211
SILValue root = rcfi.getRCIdentityRoot(rvi->getOperand());
194212
SmallVector<Argument, 8> inferredArgs;
195-
bool foundArgs = valueToDeclInferrer.infer(
196-
ArgumentKeyKind::Note, "on value:", root, [&](Argument arg) {
197-
inferredArgs.push_back(arg);
198-
return true;
199-
});
213+
bool foundArgs = valueToDeclInferrer.infer(ArgumentKeyKind::Note, root,
214+
[&](Argument arg) {
215+
inferredArgs.push_back(arg);
216+
return true;
217+
});
200218
(void)foundArgs;
201219

202220
// Retains begin a lifetime scope, so we infer scan forwards.
203-
auto remark = RemarkMissed("memory-management", *rvi,
221+
auto remark = RemarkMissed("memory", *rvi,
204222
SourceLocInferenceBehavior::ForwardScanOnly)
205-
<< "Found retain:";
206-
if (inferredArgs.empty()) {
207-
remark << Argument({ArgumentKeyKind::ParentLocNote, "InferValueFailure"},
208-
"Unable to infer any values being retained.");
209-
} else {
210-
for (auto arg : inferredArgs) {
211-
remark << arg;
212-
}
223+
<< "retain";
224+
for (auto arg : inferredArgs) {
225+
remark << arg;
213226
}
214227
return remark;
215228
});
@@ -221,26 +234,20 @@ void OptRemarkGeneratorInstructionVisitor::visitReleaseValueInst(
221234
using namespace OptRemark;
222235
SILValue root = rcfi.getRCIdentityRoot(rvi->getOperand());
223236
SmallVector<Argument, 8> inferredArgs;
224-
bool foundArgs = valueToDeclInferrer.infer(
225-
ArgumentKeyKind::Note, "on value:", root, [&](Argument arg) {
226-
inferredArgs.push_back(arg);
227-
return true;
228-
});
237+
bool foundArgs = valueToDeclInferrer.infer(ArgumentKeyKind::Note, root,
238+
[&](Argument arg) {
239+
inferredArgs.push_back(arg);
240+
return true;
241+
});
229242
(void)foundArgs;
230243

231244
// Releases end a lifetime scope so we infer scan backward.
232-
auto remark = RemarkMissed("memory-management", *rvi,
245+
auto remark = RemarkMissed("memory", *rvi,
233246
SourceLocInferenceBehavior::BackwardScanOnly)
234-
<< "Found release:";
235-
if (inferredArgs.empty()) {
236-
remark << Argument({ArgumentKeyKind::ParentLocNote, "InferValueFailure"},
237-
"Unable to infer any values being released.");
238-
} else {
239-
for (auto arg : inferredArgs) {
240-
remark << arg;
241-
}
247+
<< "release";
248+
for (auto arg : inferredArgs) {
249+
remark << arg;
242250
}
243-
244251
return remark;
245252
});
246253
}
@@ -261,7 +268,9 @@ class OptRemarkGenerator : public SILFunctionTransform {
261268

262269
return bool(langOpts.OptimizationRemarkMissedPattern) ||
263270
bool(langOpts.OptimizationRemarkPassedPattern) ||
264-
fn->getModule().getSILRemarkStreamer();
271+
fn->getModule().getSILRemarkStreamer() ||
272+
fn->hasSemanticsAttrThatStartsWith(
273+
semantics::FORCE_EMIT_OPT_REMARK_PREFIX);
265274
}
266275

267276
/// The entry point to the transformation.

lib/SILOptimizer/Transforms/PerformanceInliner.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,13 @@ bool SILPerformanceInliner::isProfitableToInline(
298298
// It is always OK to inline a simple call.
299299
// TODO: May be consider also the size of the callee?
300300
if (isPureCall(AI, SEA)) {
301+
OptRemark::Emitter::emitOrDebug(DEBUG_TYPE, &ORE, [&]() {
302+
using namespace OptRemark;
303+
return RemarkPassed("Inline", *AI.getInstruction())
304+
<< "Pure call. Always profitable to inline "
305+
<< NV("Callee", Callee);
306+
});
307+
301308
LLVM_DEBUG(dumpCaller(AI.getFunction());
302309
llvm::dbgs() << " pure-call decision " << Callee->getName()
303310
<< '\n');
@@ -487,9 +494,24 @@ bool SILPerformanceInliner::isProfitableToInline(
487494
auto *bb = AI.getInstruction()->getParent();
488495
auto bbIt = BBToWeightMap.find(bb);
489496
if (bbIt != BBToWeightMap.end()) {
490-
return profileBasedDecision(AI, Benefit, Callee, CalleeCost,
491-
NumCallerBlocks, bbIt);
497+
if (profileBasedDecision(AI, Benefit, Callee, CalleeCost, NumCallerBlocks,
498+
bbIt)) {
499+
OptRemark::Emitter::emitOrDebug(DEBUG_TYPE, &ORE, [&]() {
500+
using namespace OptRemark;
501+
return RemarkPassed("Inline", *AI.getInstruction())
502+
<< "Profitable due to provided profile";
503+
});
504+
return true;
505+
}
506+
507+
OptRemark::Emitter::emitOrDebug(DEBUG_TYPE, &ORE, [&]() {
508+
using namespace OptRemark;
509+
return RemarkMissed("Inline", *AI.getInstruction())
510+
<< "Not profitable due to provided profile";
511+
});
512+
return false;
492513
}
514+
493515
if (isClassMethodAtOsize && Benefit > OSizeClassMethodBenefit) {
494516
Benefit = OSizeClassMethodBenefit;
495517
}
@@ -498,7 +520,7 @@ bool SILPerformanceInliner::isProfitableToInline(
498520
if (CalleeCost > Benefit) {
499521
OptRemark::Emitter::emitOrDebug(DEBUG_TYPE, &ORE, [&]() {
500522
using namespace OptRemark;
501-
return RemarkMissed("NoInlinedCost", *AI.getInstruction())
523+
return RemarkMissed("Inline", *AI.getInstruction())
502524
<< "Not profitable to inline function " << NV("Callee", Callee)
503525
<< " (cost = " << NV("Cost", CalleeCost)
504526
<< ", benefit = " << NV("Benefit", Benefit) << ")";

0 commit comments

Comments
 (0)