Skip to content

Commit 86e87c2

Browse files
Add false positive detection to sil lost variables
This patch adds false positive detection to sil-stats-lost-variables. We will now only detect a debug_value as lost if there is a real instruction which belongs to the same scope or a child scope of the scope of the debug_value and if they are both inline at the same location. //a
1 parent 2e33a9a commit 86e87c2

File tree

2 files changed

+202
-19
lines changed

2 files changed

+202
-19
lines changed

lib/SILOptimizer/Utils/OptimizerStatsUtils.cpp

Lines changed: 105 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -276,15 +276,22 @@ llvm::cl::opt<std::string> StatsOnlyFunctionsNamePattern(
276276
struct FunctionStat {
277277
int BlockCount = 0;
278278
int InstCount = 0;
279+
/// True when the FunctionStat is created for a SIL Function after a SIL
280+
/// Optimization pass has been run on it. When it is a post optimization SIL
281+
/// Function, we do not want to store anything in the InlinedAts Map in
282+
/// InstCountVisitor.
283+
bool NewFunc = false;
279284
/// Instruction counts per SILInstruction kind.
280285
InstructionCounts InstCounts;
281286

282287
using VarID = std::tuple<const SILDebugScope *, llvm::StringRef,
283288
unsigned, unsigned>;
284289
llvm::StringSet<> VarNames;
285290
llvm::DenseSet<FunctionStat::VarID> DebugVariables;
291+
llvm::DenseSet<const SILDebugScope *> VisitedScope;
292+
llvm::DenseMap<VarID, const SILDebugScope *> InlinedAts;
286293

287-
FunctionStat(SILFunction *F);
294+
FunctionStat(SILFunction *F, bool NewFunc = false);
288295
FunctionStat() {}
289296

290297
// The DebugVariables set contains pointers to VarNames. Disallow copy.
@@ -384,15 +391,24 @@ struct ModuleStat {
384391
struct InstCountVisitor : SILInstructionVisitor<InstCountVisitor> {
385392
int BlockCount = 0;
386393
int InstCount = 0;
394+
/// True when the InstCountVisitor is created for a SIL Function after a SIL
395+
/// Optimization pass has been run on it. When it is a post optimization SIL
396+
/// Function, we do not want to store anything in the InlinedAts Map in
397+
/// InstCountVisitor.
398+
const bool &NewFunc;
387399
InstructionCounts &InstCounts;
388400

389401
llvm::StringSet<> &VarNames;
390402
llvm::DenseSet<FunctionStat::VarID> &DebugVariables;
403+
llvm::DenseMap<FunctionStat::VarID, const SILDebugScope *> &InlinedAts;
391404

392-
InstCountVisitor(InstructionCounts &InstCounts,
393-
llvm::StringSet<> &VarNames,
394-
llvm::DenseSet<FunctionStat::VarID> &DebugVariables)
395-
: InstCounts(InstCounts), VarNames(VarNames), DebugVariables(DebugVariables) {}
405+
InstCountVisitor(
406+
InstructionCounts &InstCounts, llvm::StringSet<> &VarNames,
407+
llvm::DenseSet<FunctionStat::VarID> &DebugVariables,
408+
llvm::DenseMap<FunctionStat::VarID, const SILDebugScope *> &InlinedAts,
409+
bool &NewFunc)
410+
: NewFunc(NewFunc), InstCounts(InstCounts), VarNames(VarNames),
411+
DebugVariables(DebugVariables), InlinedAts(InlinedAts) {}
396412

397413
int getBlockCount() const {
398414
return BlockCount;
@@ -431,6 +447,8 @@ struct InstCountVisitor : SILInstructionVisitor<InstCountVisitor> {
431447
varInfo->Scope ? varInfo->Scope : inst->getDebugScope(),
432448
UniqueName, line, col);
433449
DebugVariables.insert(key);
450+
if (!NewFunc)
451+
InlinedAts.try_emplace(key, varInfo->Scope->InlinedCallSite);
434452
}
435453
};
436454

@@ -710,16 +728,85 @@ bool isFirstTimeData(int Old, int New) {
710728
return Old == 0 && New != Old;
711729
}
712730

713-
int computeLostVariables(llvm::DenseSet<FunctionStat::VarID> &Old,
714-
llvm::DenseSet<FunctionStat::VarID> &New) {
715-
unsigned count = 0;
716-
for (auto &var : Old) {
717-
if (New.contains(var))
731+
/// Return true if \p Scope is the same as \p DbgValScope or a child scope of
732+
/// \p DbgValScope, return false otherwise.
733+
bool isScopeChildOfOrEqualTo(const SILDebugScope *Scope,
734+
const SILDebugScope *DbgValScope) {
735+
llvm::DenseSet<const SILDebugScope *> VisitedScope;
736+
while (Scope != nullptr) {
737+
if (VisitedScope.find(Scope) == VisitedScope.end()) {
738+
VisitedScope.insert(Scope);
739+
if (Scope == DbgValScope) {
740+
return true;
741+
}
742+
if (auto *S = dyn_cast<const SILDebugScope *>(Scope->Parent))
743+
Scope = S;
744+
} else
745+
return false;
746+
}
747+
return false;
748+
}
749+
750+
/// Return true if \p InlinedAt is the same as \p DbgValInlinedAt or part of
751+
/// the InlinedAt chain, return false otherwise.
752+
bool isInlinedAtChildOfOrEqualTo(const SILDebugScope *InlinedAt,
753+
const SILDebugScope *DbgValInlinedAt) {
754+
if (DbgValInlinedAt == InlinedAt)
755+
return true;
756+
if (!DbgValInlinedAt)
757+
return false;
758+
if (!InlinedAt)
759+
return false;
760+
auto *IA = InlinedAt;
761+
while (IA) {
762+
if (IA == DbgValInlinedAt)
763+
return true;
764+
IA = IA->InlinedCallSite;
765+
}
766+
return false;
767+
}
768+
769+
int computeLostVariables(SILFunction *F, FunctionStat &Old, FunctionStat &New) {
770+
unsigned LostCount = 0;
771+
unsigned PrevLostCount = 0;
772+
auto &OldSet = Old.DebugVariables;
773+
auto &NewSet = New.DebugVariables;
774+
// Find an Instruction that shares the same scope as the dropped debug_value
775+
// or has a scope that is the child of the scope of the debug_value, and has
776+
// an inlinedAt equal to the inlinedAt of the debug_value or it's inlinedAt
777+
// chain contains the inlinedAt of the debug_value, if such an Instruction is
778+
// found, debug information is dropped.
779+
for (auto &Var : OldSet) {
780+
if (NewSet.contains(Var))
718781
continue;
719-
// llvm::dbgs() << "Lost variable: " << std::get<1>(var) << "\n";
720-
count++;
782+
auto &DbgValScope = std::get<0>(Var);
783+
for (auto &BB : *F) {
784+
for (auto &I : BB) {
785+
if (I.isDebugInstruction()) {
786+
auto DbgLoc = I.getDebugLocation();
787+
auto Scope = DbgLoc.getScope();
788+
// If the Scope is a child of, or equal to the DbgValScope and is
789+
// inlined at the Var's InlinedAt location, return true to signify
790+
// that the Var has been dropped.
791+
if (isScopeChildOfOrEqualTo(Scope, DbgValScope)) {
792+
if (isInlinedAtChildOfOrEqualTo(Scope->InlinedCallSite,
793+
Old.InlinedAts[Var])) {
794+
// Found another instruction in the variable's scope, so there
795+
// exists a break point at which the variable could be observed.
796+
// Count it as dropped.
797+
LostCount++;
798+
break;
799+
}
800+
}
801+
}
802+
}
803+
if (PrevLostCount != LostCount) {
804+
PrevLostCount = LostCount;
805+
break;
806+
}
807+
}
721808
}
722-
return count;
809+
return LostCount;
723810
}
724811

725812
/// Dump statistics for a SILFunction. It is only used if a user asked to
@@ -782,8 +869,7 @@ void processFuncStatsChanges(SILFunction *F, FunctionStat &OldStat,
782869
// Compute deltas.
783870
double DeltaBlockCount = computeDelta(OldStat.BlockCount, NewStat.BlockCount);
784871
double DeltaInstCount = computeDelta(OldStat.InstCount, NewStat.InstCount);
785-
int LostVariables = computeLostVariables(OldStat.DebugVariables,
786-
NewStat.DebugVariables);
872+
int LostVariables = computeLostVariables(F, OldStat, NewStat);
787873

788874
NewLineInserter nl;
789875

@@ -959,7 +1045,7 @@ void OptimizerStatsAnalysis::updateModuleStats(TransformationContext &Ctx) {
9591045
InvalidatedFuncs.pop_back();
9601046
auto &FuncStat = getFunctionStat(F);
9611047
auto &OldFuncStat = FuncStat;
962-
FunctionStat NewFuncStat(F);
1048+
FunctionStat NewFuncStat(F, true);
9631049
processFuncStatsChanges(F, OldFuncStat, NewFuncStat, Ctx);
9641050
NewModStat.subFunctionStat(OldFuncStat);
9651051
NewModStat.addFunctionStat(NewFuncStat);
@@ -984,7 +1070,7 @@ void OptimizerStatsAnalysis::updateModuleStats(TransformationContext &Ctx) {
9841070
AddedFuncs.pop_back();
9851071
auto &FuncStat = getFunctionStat(F);
9861072
FunctionStat OldFuncStat;
987-
FunctionStat NewFuncStat(F);
1073+
FunctionStat NewFuncStat(F, true);
9881074
processFuncStatsChanges(F, OldFuncStat, NewFuncStat, Ctx);
9891075
NewModStat.addFunctionStat(NewFuncStat);
9901076
FuncStat = std::move(NewFuncStat);
@@ -1003,8 +1089,8 @@ void OptimizerStatsAnalysis::updateModuleStats(TransformationContext &Ctx) {
10031089
ModStat = NewModStat;
10041090
}
10051091

1006-
FunctionStat::FunctionStat(SILFunction *F) {
1007-
InstCountVisitor V(InstCounts, VarNames, DebugVariables);
1092+
FunctionStat::FunctionStat(SILFunction *F, bool NewFunc) : NewFunc(NewFunc) {
1093+
InstCountVisitor V(InstCounts, VarNames, DebugVariables, InlinedAts, NewFunc);
10081094
V.visitSILFunction(F);
10091095
BlockCount = V.getBlockCount();
10101096
InstCount = V.getInstCount();

test/DebugInfo/dropped-var.sil

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// RUN: %target-sil-opt --diagnose-unreachable -o - %s -sil-stats-lost-variables 2>&1 | %FileCheck %s
2+
3+
// CHECK: function, lostvars, Pass List Pipeline, DiagnoseUnreachable, 1, 1, {{[0-9]+}}, $s4test3bar1yS2i_tF
4+
5+
sil_stage raw
6+
7+
import Builtin
8+
import Swift
9+
import SwiftShims
10+
11+
sil_scope 1 { loc "file.swift":1:6 parent @$s4test3bar1yS2i_tF : $@convention(thin) (Int) -> Int }
12+
13+
// main
14+
// Isolation: unspecified
15+
sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
16+
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
17+
%2 = integer_literal $Builtin.Int32, 0 // user: %3; auto_gen
18+
%3 = struct $Int32 (%2 : $Builtin.Int32) // user: %4; auto_gen
19+
return %3 : $Int32 // id: %4; auto_gen
20+
} // end sil function 'main'
21+
22+
// bar(y:)
23+
// Isolation: unspecified
24+
sil hidden [ossa] @$s4test3bar1yS2i_tF : $@convention(thin) (Int) -> Int {
25+
// %0 "y" // users: %5, %1
26+
bb0(%0 : $Int):
27+
debug_value %0 : $Int, let, name "y", argno 1 // id: %1; line:1:10:in_prologue
28+
%2 = integer_literal $Builtin.Int1, 0 // user: %3; line:2:11:minlined
29+
cond_br %2, bb1, bb2 // id: %3; line:2:11
30+
31+
bb1: // Preds: bb0
32+
%4 = integer_literal $Builtin.Int64, 1 // user: %7; line:3:20:minlined
33+
debug_value %4 : $Builtin.Int64, let, name "num"
34+
%5 = struct_extract %0 : $Int, #Int._value // user: %7; line:3:18:minlined
35+
%6 = integer_literal $Builtin.Int1, -1 // user: %7; line:3:18:minlined
36+
%7 = builtin "sadd_with_overflow_Int64"(%5 : $Builtin.Int64, %4 : $Builtin.Int64, %6 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) // user: %8; line:3:18:minlined
37+
(%8, %9) = destructure_tuple %7 : $(Builtin.Int64, Builtin.Int1) // users: %11, %10; line:3:18:minlined
38+
cond_fail %9 : $Builtin.Int1, "arithmetic overflow" // id: %10; line:3:18:minlined
39+
%11 = struct $Int (%8 : $Builtin.Int64) // user: %12; line:3:18:minlined
40+
br bb3(%11 : $Int) // id: %12; line:3:9:return
41+
bb2: // Preds: bb0
42+
%13 = integer_literal $Builtin.Int64, 1 // user: %14; line:5:12:minlined
43+
%14 = struct $Int (%13 : $Builtin.Int64) // user: %15; line:5:12:minlined
44+
br bb3(%14 : $Int) // id: %15; line:5:5:return
45+
46+
// %16 // user: %17
47+
bb3(%16 : $Int): // Preds: bb2 bb1
48+
return %16 : $Int // id: %17; line:6:1:cleanup
49+
50+
// Do not count as dropped variable if there is no instruction that belongs to the same scope as lost debug_value
51+
bb4:
52+
%17 = integer_literal $Builtin.Int64, 1, loc "file.swift":1:6, scope 1
53+
debug_value %17 : $Builtin.Int64, let, name "num", loc "file.swift":1:6, scope 1
54+
%18 = struct $Int (%17 : $Builtin.Int64)
55+
br bb3(%18: $Int)
56+
57+
} // end sil function '$s4test3bar1yS2i_tF'
58+
59+
// Int.init(_builtinIntegerLiteral:)
60+
// Isolation: unspecified
61+
sil public_external [transparent] [serialized] [canonical] [ossa] @$sSi22_builtinIntegerLiteralSiBI_tcfC : $@convention(method) (Builtin.IntLiteral, @thin Int.Type) -> Int {
62+
// %0 // user: %2
63+
bb0(%0 : $Builtin.IntLiteral, %1 : $@thin Int.Type):
64+
%2 = builtin "s_to_s_checked_trunc_IntLiteral_Int64"(%0 : $Builtin.IntLiteral) : $(Builtin.Int64, Builtin.Int1) // user: %3; no_loc
65+
(%3, %4) = destructure_tuple %2 : $(Builtin.Int64, Builtin.Int1) // user: %5; no_loc
66+
%5 = struct $Int (%3 : $Builtin.Int64) // user: %6; no_loc
67+
return %5 : $Int // id: %6; no_loc
68+
} // end sil function '$sSi22_builtinIntegerLiteralSiBI_tcfC'
69+
70+
// static Int.> infix(_:_:)
71+
// Isolation: unspecified
72+
sil public_external [transparent] [serialized] [canonical] [ossa] @$sSi1goiySbSi_SitFZ : $@convention(method) (Int, Int, @thin Int.Type) -> Bool {
73+
// %0 // user: %4
74+
// %1 // user: %3
75+
bb0(%0 : $Int, %1 : $Int, %2 : $@thin Int.Type):
76+
%3 = struct_extract %1 : $Int, #Int._value // user: %5; no_loc
77+
%4 = struct_extract %0 : $Int, #Int._value // user: %5; no_loc
78+
%5 = builtin "cmp_slt_Int64"(%3 : $Builtin.Int64, %4 : $Builtin.Int64) : $Builtin.Int1 // user: %6; no_loc
79+
%6 = struct $Bool (%5 : $Builtin.Int1) // user: %7; no_loc
80+
return %6 : $Bool // id: %7; no_loc
81+
} // end sil function '$sSi1goiySbSi_SitFZ'
82+
83+
// static Int.+ infix(_:_:)
84+
// Isolation: unspecified
85+
sil public_external [transparent] [serialized] [canonical] [ossa] @$sSi1poiyS2i_SitFZ : $@convention(method) (Int, Int, @thin Int.Type) -> Int {
86+
// %0 // user: %3
87+
// %1 // user: %4
88+
bb0(%0 : $Int, %1 : $Int, %2 : $@thin Int.Type):
89+
%3 = struct_extract %0 : $Int, #Int._value // user: %6; no_loc
90+
%4 = struct_extract %1 : $Int, #Int._value // user: %6; no_loc
91+
%5 = integer_literal $Builtin.Int1, -1 // user: %6; no_loc
92+
%6 = builtin "sadd_with_overflow_Int64"(%3 : $Builtin.Int64, %4 : $Builtin.Int64, %5 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1) // user: %7; no_loc
93+
(%7, %8) = destructure_tuple %6 : $(Builtin.Int64, Builtin.Int1) // users: %10, %9; no_loc
94+
cond_fail %8 : $Builtin.Int1, "arithmetic overflow" // id: %9; no_loc
95+
%10 = struct $Int (%7 : $Builtin.Int64) // user: %11; no_loc
96+
return %10 : $Int // id: %11; no_loc
97+
} // end sil function '$sSi1poiyS2i_SitFZ'

0 commit comments

Comments
 (0)