Skip to content

Commit 753f7ab

Browse files
committed
[opt-remark] Add remarks for casts that go through the runtime and aren't optimized away.
Now we get opt-remarks like the following: ``` public func condCast5<NS, T>(_ ns: NS) -> T? { // Make sure the colon info is right so that the arrow is under the a. // // Today, we lose that x was assigned. if let x = ns as? T { // expected-remark @:17 {{conditional runtime cast of value with type 'NS' to 'T'}} return x // expected-note @-5:32 {{of 'ns'}} } return nil } ```
1 parent d9f2c1e commit 753f7ab

File tree

3 files changed

+618
-10
lines changed

3 files changed

+618
-10
lines changed

lib/SILOptimizer/Transforms/OptRemarkGenerator.cpp

Lines changed: 84 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919

2020
#define DEBUG_TYPE "sil-opt-remark-gen"
2121

22-
#include "swift/Basic/Defer.h"
2322
#include "swift/AST/SemanticAttrs.h"
23+
#include "swift/Basic/Defer.h"
24+
#include "swift/SIL/DebugUtils.h"
25+
#include "swift/SIL/DynamicCasts.h"
2426
#include "swift/SIL/MemAccessUtils.h"
2527
#include "swift/SIL/OptimizationRemark.h"
2628
#include "swift/SIL/Projection.h"
@@ -244,19 +246,20 @@ bool ValueUseToDeclInferrer::findDecls(Operand *use, SILValue value) {
244246
// Then see if we have a debug_value that is associated with a non-inlined
245247
// debug scope. Such an instruction is an instruction that is from the
246248
// current function.
247-
auto *dvi = dyn_cast<DebugValueInst>(use->getUser());
248-
if (!dvi)
249+
auto debugInst = DebugVarCarryingInst(use->getUser());
250+
if (!debugInst)
249251
return false;
250252

251-
if (!hasNonInlinedDebugScope(dvi))
253+
LLVM_DEBUG(llvm::dbgs() << "Found DebugInst: " << **debugInst);
254+
if (!hasNonInlinedDebugScope(*debugInst))
252255
return false;
253256

254257
// See if we have already inferred this debug_value as a potential source
255258
// for this instruction. In such a case, just return.
256-
if (!visitedDebugValueInsts.insert(dvi).second)
259+
if (!visitedDebugValueInsts.insert(*debugInst).second)
257260
return false;
258261

259-
if (auto *decl = dvi->getDecl()) {
262+
if (auto *decl = debugInst.getDecl()) {
260263
std::string msg;
261264
{
262265
llvm::raw_string_ostream stream(msg);
@@ -279,7 +282,7 @@ bool ValueUseToDeclInferrer::findDecls(Operand *use, SILValue value) {
279282
if (!DecllessDebugValueUseSILDebugInfo)
280283
return false;
281284

282-
auto varInfo = dvi->getVarInfo();
285+
auto varInfo = debugInst.getVarInfo();
283286
if (!varInfo)
284287
return false;
285288

@@ -293,8 +296,8 @@ bool ValueUseToDeclInferrer::findDecls(Operand *use, SILValue value) {
293296
object.printNote(stream, name,
294297
use->get() == value /*print projection path*/);
295298
}
296-
resultingInferredDecls.push_back(
297-
Argument({keyKind, "InferredValue"}, std::move(msg), dvi->getLoc()));
299+
resultingInferredDecls.push_back(Argument(
300+
{keyKind, "InferredValue"}, std::move(msg), debugInst->getLoc()));
298301
return true;
299302
}
300303

@@ -315,7 +318,10 @@ bool ValueToDeclInferrer::infer(
315318
// the bottom of the while loop where we always return true (since we did not
316319
// hit a could not compute case). Reassign value and continue to go to the
317320
// next step.
321+
LLVM_DEBUG(llvm::dbgs() << "Searching for decls!\n");
318322
while (true) {
323+
LLVM_DEBUG(llvm::dbgs() << "Visiting: " << *value);
324+
319325
// First check for "identified values" like arguments and global_addr.
320326
if (auto *arg = dyn_cast<SILArgument>(value))
321327
if (auto *decl = arg->getDecl()) {
@@ -368,6 +374,10 @@ bool ValueToDeclInferrer::infer(
368374
}
369375
}
370376

377+
// We prefer decls not from uses since these are inherently noisier. Still,
378+
// it is better than nothing.
379+
bool foundDeclFromUse = false;
380+
371381
if (auto *asi = dyn_cast<AllocStackInst>(value)) {
372382
if (auto *decl = asi->getDecl()) {
373383
std::string msg;
@@ -379,6 +389,23 @@ bool ValueToDeclInferrer::infer(
379389
Argument({keyKind, "InferredValue"}, std::move(msg), decl));
380390
return true;
381391
}
392+
393+
// See if we have a single init alloc_stack and can infer a
394+
// debug_value/debug_value_addr from that.
395+
LLVM_DEBUG(llvm::dbgs() << "Checking for single init use!\n");
396+
if (auto *initUse = getSingleInitAllocStackUse(asi)) {
397+
LLVM_DEBUG(llvm::dbgs() << "Found one: " << *initUse->getUser());
398+
if (auto *si = dyn_cast<StoreInst>(initUse->getUser())) {
399+
for (auto *use : si->getSrc()->getUses()) {
400+
foundDeclFromUse |= valueUseInferrer.findDecls(use, value);
401+
}
402+
}
403+
if (auto *cai = dyn_cast<CopyAddrInst>(initUse->getUser())) {
404+
for (auto *use : cai->getSrc()->getUses()) {
405+
foundDeclFromUse |= valueUseInferrer.findDecls(use, value);
406+
}
407+
}
408+
}
382409
}
383410

384411
// Then visit our users (ignoring rc identical transformations) and see if
@@ -388,7 +415,6 @@ bool ValueToDeclInferrer::infer(
388415
// The reason why we do this is that sometimes we reform a struct from its
389416
// constituant parts and then construct the debug_value from that. For
390417
// instance, if we FSOed.
391-
bool foundDeclFromUse = false;
392418
rcfi.visitRCUses(value, [&](Operand *use) {
393419
foundDeclFromUse |= valueUseInferrer.findDecls(use, value);
394420
});
@@ -460,10 +486,58 @@ struct OptRemarkGeneratorInstructionVisitor
460486
void visitSILInstruction(SILInstruction *) {}
461487
void visitBeginAccessInst(BeginAccessInst *bai);
462488
void visitEndAccessInst(EndAccessInst *eai);
489+
void visitCheckedCastAddrBranchInst(CheckedCastAddrBranchInst *ccabi);
490+
void visitUnconditionalCheckedCastAddrInst(
491+
UnconditionalCheckedCastAddrInst *uccai);
463492
};
464493

465494
} // anonymous namespace
466495

496+
void OptRemarkGeneratorInstructionVisitor::
497+
visitUnconditionalCheckedCastAddrInst(
498+
UnconditionalCheckedCastAddrInst *uccai) {
499+
ORE.emit([&]() {
500+
using namespace OptRemark;
501+
SmallVector<Argument, 8> inferredArgs;
502+
bool foundArgs = valueToDeclInferrer.infer(
503+
ArgumentKeyKind::Note, uccai->getSrc(), inferredArgs,
504+
true /*allow single ref elt peek*/);
505+
(void)foundArgs;
506+
507+
// Use the actual source loc of the
508+
auto remark = RemarkMissed("memory", *uccai)
509+
<< "unconditional runtime cast of value with type '"
510+
<< NV("ValueType", uccai->getSrc()->getType()) << "' to '"
511+
<< NV("CastType", uccai->getDest()->getType()) << "'";
512+
for (auto arg : inferredArgs) {
513+
remark << arg;
514+
}
515+
return remark;
516+
});
517+
}
518+
519+
void OptRemarkGeneratorInstructionVisitor::visitCheckedCastAddrBranchInst(
520+
CheckedCastAddrBranchInst *ccabi) {
521+
ORE.emit([&]() {
522+
using namespace OptRemark;
523+
SmallVector<Argument, 8> inferredArgs;
524+
bool foundArgs = valueToDeclInferrer.infer(
525+
ArgumentKeyKind::Note, ccabi->getSrc(), inferredArgs,
526+
true /*allow single ref elt peek*/);
527+
(void)foundArgs;
528+
529+
// Use the actual source loc of the
530+
auto remark = RemarkMissed("memory", *ccabi)
531+
<< "conditional runtime cast of value with type '"
532+
<< NV("ValueType", ccabi->getSrc()->getType()) << "' to '"
533+
<< NV("CastType", ccabi->getDest()->getType()) << "'";
534+
for (auto arg : inferredArgs) {
535+
remark << arg;
536+
}
537+
return remark;
538+
});
539+
}
540+
467541
void OptRemarkGeneratorInstructionVisitor::visitBeginAccessInst(
468542
BeginAccessInst *bai) {
469543
ORE.emit([&]() {

0 commit comments

Comments
 (0)