Skip to content

Commit f9372fd

Browse files
committed
[DebugInfo] Improve stepping behavior for switch case stmts
Assign the location of a switch statement's subject expression to all of its case statements. This improves the debugger's stepping behavior in switch statements. Stepping into a switch now goes directly to the first matching case (possibly one with a `where` clause that may or may not match). It's still possible to set breakpoints within `where` clauses. rdar://35628672
1 parent a0d9c23 commit f9372fd

File tree

5 files changed

+178
-13
lines changed

5 files changed

+178
-13
lines changed

include/swift/SIL/SILBuilder.h

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class SILBuilder {
4747
SILBasicBlock *BB;
4848
SILBasicBlock::iterator InsertPt;
4949
const SILDebugScope *CurDebugScope = nullptr;
50+
Optional<SILLocation> CurDebugLocOverride = None;
5051

5152
/// If this pointer is non-null, then any inserted instruction is
5253
/// recorded in this list.
@@ -141,14 +142,27 @@ class SILBuilder {
141142
void setCurrentDebugScope(const SILDebugScope *DS) { CurDebugScope = DS; }
142143
const SILDebugScope *getCurrentDebugScope() const { return CurDebugScope; }
143144

145+
/// Apply a debug location override. If loc is None, the current override is
146+
/// removed. Otherwise, newly created debug locations use the given location.
147+
/// Note: the override location does not apply to debug_value[_addr].
148+
void applyDebugLocOverride(Optional<SILLocation> loc) {
149+
CurDebugLocOverride = loc;
150+
}
151+
152+
/// Get the current debug location override.
153+
Optional<SILLocation> getCurrentDebugLocOverride() const {
154+
return CurDebugLocOverride;
155+
}
156+
144157
/// Convenience function for building a SILDebugLocation.
145158
SILDebugLocation getSILDebugLocation(SILLocation Loc) {
146159
// FIXME: Audit all uses and enable this assertion.
147160
// assert(getCurrentDebugScope() && "no debug scope");
148161
auto Scope = getCurrentDebugScope();
149162
if (!Scope && F)
150163
Scope = F->getDebugScope();
151-
return SILDebugLocation(Loc, Scope);
164+
auto overriddenLoc = CurDebugLocOverride ? *CurDebugLocOverride : Loc;
165+
return SILDebugLocation(overriddenLoc, Scope);
152166
}
153167

154168
//===--------------------------------------------------------------------===//
@@ -733,16 +747,9 @@ class SILBuilder {
733747
}
734748

735749
DebugValueInst *createDebugValue(SILLocation Loc, SILValue src,
736-
SILDebugVariable Var) {
737-
return insert(DebugValueInst::create(getSILDebugLocation(Loc), src,
738-
getModule(), Var));
739-
}
740-
DebugValueAddrInst *
741-
createDebugValueAddr(SILLocation Loc, SILValue src,
742-
SILDebugVariable Var) {
743-
return insert(DebugValueAddrInst::create(getSILDebugLocation(Loc), src,
744-
getModule(), Var));
745-
}
750+
SILDebugVariable Var);
751+
DebugValueAddrInst *createDebugValueAddr(SILLocation Loc, SILValue src,
752+
SILDebugVariable Var);
746753

747754
LoadWeakInst *createLoadWeak(SILLocation Loc, SILValue src, IsTake_t isTake) {
748755
return insert(new (getModule())
@@ -2095,6 +2102,35 @@ class SavedInsertionPointRAII {
20952102
}
20962103
};
20972104

2105+
/// Apply a debug location override for the duration of the current scope.
2106+
class DebugLocOverrideRAII {
2107+
SILBuilder &Builder;
2108+
Optional<SILLocation> oldOverride;
2109+
#ifndef NDEBUG
2110+
Optional<SILLocation> installedOverride;
2111+
#endif
2112+
2113+
public:
2114+
DebugLocOverrideRAII(SILBuilder &B, Optional<SILLocation> Loc) : Builder(B) {
2115+
oldOverride = B.getCurrentDebugLocOverride();
2116+
Builder.applyDebugLocOverride(Loc);
2117+
#ifndef NDEBUG
2118+
installedOverride = Loc;
2119+
#endif
2120+
}
2121+
2122+
~DebugLocOverrideRAII() {
2123+
assert(Builder.getCurrentDebugLocOverride() == installedOverride &&
2124+
"Restoring debug location override to an unexpected state");
2125+
Builder.applyDebugLocOverride(oldOverride);
2126+
}
2127+
2128+
DebugLocOverrideRAII(const DebugLocOverrideRAII &) = delete;
2129+
DebugLocOverrideRAII &operator=(const DebugLocOverrideRAII &) = delete;
2130+
DebugLocOverrideRAII(DebugLocOverrideRAII &&) = delete;
2131+
DebugLocOverrideRAII &operator=(DebugLocOverrideRAII &&) = delete;
2132+
};
2133+
20982134
} // end swift namespace
20992135

21002136
#endif

lib/SIL/SILBuilder.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,3 +521,20 @@ void SILBuilder::emitShallowDestructureAddressOperation(
521521
return P.createAddressProjection(*this, Loc, V).get();
522522
});
523523
}
524+
525+
DebugValueInst *SILBuilder::createDebugValue(SILLocation Loc, SILValue src,
526+
SILDebugVariable Var) {
527+
// Debug location overrides cannot apply to debug value instructions.
528+
DebugLocOverrideRAII LocOverride{*this, None};
529+
return insert(
530+
DebugValueInst::create(getSILDebugLocation(Loc), src, getModule(), Var));
531+
}
532+
533+
DebugValueAddrInst *SILBuilder::createDebugValueAddr(SILLocation Loc,
534+
SILValue src,
535+
SILDebugVariable Var) {
536+
// Debug location overrides cannot apply to debug addr instructions.
537+
DebugLocOverrideRAII LocOverride{*this, None};
538+
return insert(DebugValueAddrInst::create(getSILDebugLocation(Loc), src,
539+
getModule(), Var));
540+
}

lib/SILGen/SILGenPattern.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,13 @@ class PatternMatchEmission {
421421
: SGF(SGF), PatternMatchStmt(S),
422422
CompletionHandler(completionHandler) {}
423423

424+
Optional<SILLocation> getSubjectLocationOverride(SILLocation loc) const {
425+
if (auto *Switch = dyn_cast<SwitchStmt>(PatternMatchStmt))
426+
if (!Switch->isImplicit())
427+
return SILLocation(Switch->getSubjectExpr());
428+
return None;
429+
}
430+
424431
void emitDispatch(ClauseMatrix &matrix, ArgArray args,
425432
const FailureHandler &failure);
426433

@@ -1144,6 +1151,7 @@ void PatternMatchEmission::bindRefutablePattern(Pattern *pattern,
11441151
void PatternMatchEmission::bindExprPattern(ExprPattern *pattern,
11451152
ConsumableManagedValue value,
11461153
const FailureHandler &failure) {
1154+
DebugLocOverrideRAII LocOverride{SGF.B, getSubjectLocationOverride(pattern)};
11471155
FullExpr scope(SGF.Cleanups, CleanupLocation(pattern));
11481156
bindVariable(pattern, pattern->getMatchVar(), value,
11491157
pattern->getType()->getCanonicalType(),

test/SILGen/sil_locations.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,9 @@ func testSwitch() {
129129
var x:Int
130130
x = 0
131131
switch (switchfoo(), switchbar()) {
132-
// CHECK: store {{.*}}, loc "{{.*}}":[[@LINE+1]]
132+
// CHECK: store {{.*}}, loc "{{.*}}":[[@LINE-1]]
133133
case (1,2):
134-
// CHECK: integer_literal $Builtin.Int2048, 2, loc "{{.*}}":[[@LINE-1]]:11
134+
// CHECK: integer_literal $Builtin.Int2048, 2, loc "{{.*}}":[[@LINE-3]]:10
135135
// FIXME: Location info is missing.
136136
// CHECK: cond_br
137137
//

test/SILGen/switch_debuginfo.swift

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// RUN: %target-swift-frontend -g -Xllvm -sil-print-debuginfo -emit-silgen %s | %FileCheck %s
2+
3+
func nop1() {}
4+
func nop2() {}
5+
6+
enum Binary {
7+
case On
8+
case Off
9+
}
10+
11+
func isOn(_ b: Binary) -> Bool {
12+
return b == .On
13+
}
14+
15+
// CHECK: [[LOC:loc "[^"]+"]]
16+
17+
// First, check that we don't assign fresh locations to each case statement,
18+
// except for any relevant debug value instructions.
19+
// CHECK-LABEL: sil hidden @$S16switch_debuginfo5test11iySi_tF
20+
func test1(i: Int) {
21+
switch i {
22+
// CHECK-NOT: [[LOC]]:[[@LINE+1]]
23+
case 0: // CHECK: debug_value {{.*}} : $Int, let, name "$match", [[LOC]]:[[@LINE]]
24+
// CHECK-NOT: [[LOC]]:[[@LINE-1]]
25+
nop1()
26+
27+
// CHECK-NOT: [[LOC]]:[[@LINE+1]]
28+
case 1: // CHECK: debug_value {{.*}} : $Int, let, name "$match", [[LOC]]:[[@LINE]]
29+
// CHECK-NOT: [[LOC]]:[[@LINE-1]]
30+
nop1()
31+
32+
default: // CHECK-NOT: [[LOC]]:[[@LINE]]
33+
nop1()
34+
}
35+
}
36+
37+
// Next, check that case statements and switch subjects have the same locations.
38+
// CHECK-LABEL: sil hidden @$S16switch_debuginfo5test21sySS_tF
39+
func test2(s: String) {
40+
switch s {
41+
case "a": // CHECK: string_literal utf8 "a", [[LOC]]:[[@LINE-1]]:10
42+
nop1()
43+
case "b": // CHECK: string_literal utf8 "b", [[LOC]]:[[@LINE-3]]:10
44+
nop2()
45+
default:
46+
nop1()
47+
}
48+
}
49+
50+
// Fallthrough shouldn't affect case statement locations.
51+
// CHECK-LABEL: sil hidden @$S16switch_debuginfo5test31sySS_tF
52+
func test3(s: String) {
53+
switch s {
54+
case "a", "b":
55+
// CHECK: string_literal utf8 "a", [[LOC]]:[[@LINE-2]]:10
56+
// CHECK: string_literal utf8 "b", [[LOC]]:[[@LINE-3]]:10
57+
nop1()
58+
fallthrough
59+
case "b", "c":
60+
// CHECK: string_literal utf8 "b", [[LOC]]:[[@LINE-7]]:10
61+
// CHECK: string_literal utf8 "c", [[LOC]]:[[@LINE-8]]:10
62+
nop2()
63+
fallthrough
64+
default:
65+
nop1()
66+
}
67+
}
68+
69+
// It should be possible to set breakpoints on where clauses.
70+
// CHECK-LABEL: sil hidden @$S16switch_debuginfo5test41byAA6BinaryO_tF
71+
func test4(b: Binary) {
72+
switch b {
73+
case let _ // CHECK-NOT: [[LOC]]:[[@LINE]]
74+
where isOn(b): // CHECK: [[LOC]]:[[@LINE]]:11
75+
nop1()
76+
case let _ // CHECK-NOT: [[LOC]]:[[@LINE]]
77+
where !isOn(b): // CHECK: [[LOC]]:[[@LINE]]:11
78+
nop2()
79+
default:
80+
nop1()
81+
}
82+
}
83+
84+
// Check that we set the right locations before/after nested switches.
85+
// CHECK-LABEL: sil hidden @$S16switch_debuginfo5test51sySS_tF
86+
func test5(s: String) {
87+
switch s {
88+
case "a": // CHECK: string_literal utf8 "a", [[LOC]]:[[@LINE-1]]:10
89+
switch "b" {
90+
case "b": // CHECK: string_literal utf8 "b", [[LOC]]:[[@LINE-1]]:12
91+
nop1()
92+
default:
93+
nop2()
94+
}
95+
if "c" == "c" { // CHECK: string_literal utf8 "c", [[LOC]]:[[@LINE]]
96+
nop1()
97+
}
98+
default:
99+
nop1()
100+
}
101+
if "d" == "d" { // CHECK: string_literal utf8 "d", [[LOC]]:[[@LINE]]
102+
nop1()
103+
}
104+
}

0 commit comments

Comments
 (0)