Skip to content

Commit 60ec3f1

Browse files
authored
Fix debug description for cases with multiple items (#32282)
* [SILGenFunction] Don't create redundant nested debug scopes Instead of emitting: ``` sil_scope 4 { loc "main.swift":6:19 parent 3 } sil_scope 5 { loc "main.swift":7:3 parent 4 } sil_scope 6 { loc "main.swift":7:3 parent 5 } sil_scope 7 { loc "main.swift":7:3 parent 5 } sil_scope 8 { loc "main.swift":9:5 parent 4 } ``` Emit: ``` sil_scope 4 { loc "main.swift":6:19 parent 3 } sil_scope 5 { loc "main.swift":7:3 parent 4 } sil_scope 6 { loc "main.swift":9:5 parent 5 } ``` * [IRGenSIL] Diagnose conflicting shadow copies If we attempt to store a value with the wrong type into a slot reserved for a shadow copy, diagnose what went wrong. * [SILGenPattern] Defer debug description of case variables Create unique nested debug scopes for a switch, each of its case labels, and each of its case bodies. This looks like: ``` switch ... { // Enter scope 1. case ... : // Enter scope 2, nested within scope 1. <body-1> // Enter scope 3, nested within scope 2. case ... : // Enter scope 4, nested within scope 1. <body-2> // Enter scope 5, nested within scope 4. } ``` Use the new scope structure to defer emitting debug descriptions of case bindings. Specifically, defer the work until we can nest the scope for a case body under the scope for a pattern match. This fixes SR-7973, a problem where it was impossible to inspect a case binding in lldb when stopped at a case with multiple items. Previously, we would emit the debug descriptions too early (in the pattern match), leading to duplicate/conflicting descriptions. The only reason that the ambiguous description was allowed to compile was because the debug scopes were nested incorrectly. rdar://41048339 * Update tests
1 parent 9b77762 commit 60ec3f1

20 files changed

+227
-83
lines changed

include/swift/SIL/SILBuilder.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,14 @@ class SILBuilder {
899899
DebugValueAddrInst *createDebugValueAddr(SILLocation Loc, SILValue src,
900900
SILDebugVariable Var);
901901

902+
/// Create a debug_value_addr if \p src is an address; a debug_value if not.
903+
SILInstruction *emitDebugDescription(SILLocation Loc, SILValue src,
904+
SILDebugVariable Var) {
905+
if (src->getType().isAddress())
906+
return createDebugValueAddr(Loc, src, Var);
907+
return createDebugValue(Loc, src, Var);
908+
}
909+
902910
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
903911
Load##Name##Inst *createLoad##Name(SILLocation Loc, \
904912
SILValue src, \

include/swift/SIL/SILDebugScope.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ class SILDebugScope : public SILAllocated<SILDebugScope> {
5656
/// Create a scope for an artificial function.
5757
SILDebugScope(SILLocation Loc);
5858

59+
SILLocation getLoc() const { return Loc; }
60+
5961
/// Return the function this scope originated from before being inlined.
6062
SILFunction *getInlinedFunction() const;
6163

@@ -64,12 +66,10 @@ class SILDebugScope : public SILAllocated<SILDebugScope> {
6466
/// into.
6567
SILFunction *getParentFunction() const;
6668

67-
#ifndef NDEBUG
68-
SWIFT_DEBUG_DUMPER(dump(SourceManager &SM,
69-
llvm::raw_ostream &OS = llvm::errs(),
70-
unsigned Indent = 0));
71-
SWIFT_DEBUG_DUMPER(dump(SILModule &Mod));
72-
#endif
69+
void print(SourceManager &SM, llvm::raw_ostream &OS = llvm::errs(),
70+
unsigned Indent = 0) const;
71+
72+
void print(SILModule &Mod) const;
7373
};
7474

7575
/// Determine whether an instruction may not have a SILDebugScope.

include/swift/SIL/SILLocation.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,8 @@ class SILLocation {
490490
Loc.ASTNode.ForDebugger.getOpaqueValue() ==
491491
R.Loc.ASTNode.ForDebugger.getOpaqueValue();
492492
}
493+
494+
inline bool operator!=(const SILLocation &R) const { return !(*this == R); }
493495
};
494496

495497
/// Allowed on any instruction.

lib/IRGen/IRGenSIL.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,29 @@ class IRGenSILFunction :
696696
return !isa<llvm::Constant>(Storage);
697697
}
698698

699+
#ifndef NDEBUG
700+
/// Check if \p Val can be stored into \p Alloca, and emit some diagnostic
701+
/// info if it can't.
702+
bool canAllocaStoreValue(Address Alloca, llvm::Value *Val,
703+
SILDebugVariable VarInfo,
704+
const SILDebugScope *Scope) {
705+
bool canStore =
706+
cast<llvm::PointerType>(Alloca->getType())->getElementType() ==
707+
Val->getType();
708+
if (canStore)
709+
return true;
710+
llvm::errs() << "Invalid shadow copy:\n"
711+
<< " Value : " << *Val << "\n"
712+
<< " Alloca: " << *Alloca.getAddress() << "\n"
713+
<< "---\n"
714+
<< "Previous shadow copy into " << VarInfo.Name
715+
<< " in the same scope!\n"
716+
<< "Scope:\n";
717+
Scope->print(getSILModule());
718+
return false;
719+
}
720+
#endif
721+
699722
/// Unconditionally emit a stack shadow copy of an \c llvm::Value.
700723
llvm::Value *emitShadowCopy(llvm::Value *Storage, const SILDebugScope *Scope,
701724
SILDebugVariable VarInfo, llvm::Optional<Alignment> _Align) {
@@ -706,7 +729,8 @@ class IRGenSILFunction :
706729
if (!Alloca.isValid())
707730
Alloca = createAlloca(Storage->getType(), Align, VarInfo.Name + ".debug");
708731
zeroInit(cast<llvm::AllocaInst>(Alloca.getAddress()));
709-
732+
assert(canAllocaStoreValue(Alloca, Storage, VarInfo, Scope) &&
733+
"bad scope?");
710734
ArtificialLocation AutoRestore(Scope, IGM.DebugInfo.get(), Builder);
711735
Builder.CreateStore(Storage, Alloca.getAddress(), Align);
712736
return Alloca.getAddress();

lib/SIL/IR/SILPrinter.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3381,7 +3381,7 @@ void SILCoverageMap::dump() const {
33813381
#pragma warning(disable : 4996)
33823382
#endif
33833383

3384-
void SILDebugScope::dump(SourceManager &SM, llvm::raw_ostream &OS,
3384+
void SILDebugScope::print(SourceManager &SM, llvm::raw_ostream &OS,
33853385
unsigned Indent) const {
33863386
OS << "{\n";
33873387
OS.indent(Indent);
@@ -3391,7 +3391,7 @@ void SILDebugScope::dump(SourceManager &SM, llvm::raw_ostream &OS,
33913391
OS.indent(Indent + 2);
33923392
OS << " parent: ";
33933393
if (auto *P = Parent.dyn_cast<const SILDebugScope *>()) {
3394-
P->dump(SM, OS, Indent + 2);
3394+
P->print(SM, OS, Indent + 2);
33953395
OS.indent(Indent + 2);
33963396
}
33973397
else if (auto *F = Parent.dyn_cast<SILFunction *>())
@@ -3403,15 +3403,15 @@ void SILDebugScope::dump(SourceManager &SM, llvm::raw_ostream &OS,
34033403
OS.indent(Indent + 2);
34043404
if (auto *CS = InlinedCallSite) {
34053405
OS << "inlinedCallSite: ";
3406-
CS->dump(SM, OS, Indent + 2);
3406+
CS->print(SM, OS, Indent + 2);
34073407
OS.indent(Indent + 2);
34083408
}
34093409
OS << "}\n";
34103410
}
34113411

3412-
void SILDebugScope::dump(SILModule &Mod) const {
3412+
void SILDebugScope::print(SILModule &Mod) const {
34133413
// We just use the default indent and llvm::errs().
3414-
dump(Mod.getASTContext().SourceMgr);
3414+
print(Mod.getASTContext().SourceMgr);
34153415
}
34163416

34173417
#if SWIFT_COMPILER_IS_MSVC

lib/SILGen/Initialization.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ class Initialization {
148148
ManagedValue explodedElement,
149149
bool isInit) = 0;
150150

151+
/// Whether to emit a debug value during initialization.
152+
void setEmitDebugValueOnInit(bool emit) { EmitDebugValueOnInit = emit; }
153+
151154
/// Perform post-initialization bookkeeping for this initialization.
152155
virtual void finishInitialization(SILGenFunction &SGF) {}
153156

@@ -158,6 +161,9 @@ class Initialization {
158161
"uninitialized");
159162
}
160163

164+
protected:
165+
bool EmitDebugValueOnInit = true;
166+
161167
private:
162168
Initialization(const Initialization &) = delete;
163169
Initialization(Initialization &&) = delete;

lib/SILGen/SILGenDecl.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -555,14 +555,13 @@ class LetValueInitialization : public Initialization {
555555
SGF.VarLocs[vd] = SILGenFunction::VarLoc::get(value);
556556

557557
// Emit a debug_value[_addr] instruction to record the start of this value's
558-
// lifetime.
558+
// lifetime, if permitted to do so.
559+
if (!EmitDebugValueOnInit)
560+
return;
559561
SILLocation PrologueLoc(vd);
560562
PrologueLoc.markAsPrologue();
561563
SILDebugVariable DbgVar(vd->isLet(), /*ArgNo=*/0);
562-
if (address)
563-
SGF.B.createDebugValueAddr(PrologueLoc, value, DbgVar);
564-
else
565-
SGF.B.createDebugValue(PrologueLoc, value, DbgVar);
564+
SGF.B.emitDebugDescription(PrologueLoc, value, DbgVar);
566565
}
567566

568567
void copyOrInitValueInto(SILGenFunction &SGF, SILLocation loc,

lib/SILGen/SILGenFunction.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
324324
std::vector<BreakContinueDest> BreakContinueDestStack;
325325
std::vector<PatternMatchContext*> SwitchStack;
326326
/// Keep track of our current nested scope.
327-
std::vector<SILDebugScope*> DebugScopeStack;
327+
std::vector<const SILDebugScope *> DebugScopeStack;
328328

329329
/// The cleanup depth and BB for when the operand of a
330330
/// BindOptionalExpr is a missing value.
@@ -571,12 +571,15 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
571571
StringRef getMagicFilePathString(SourceLoc loc);
572572
StringRef getMagicFunctionString();
573573

574-
/// Push a new debug scope and set its parent pointer.
574+
/// Enter the debug scope for \p Loc, creating it if necessary.
575575
void enterDebugScope(SILLocation Loc) {
576576
auto *Parent =
577577
DebugScopeStack.size() ? DebugScopeStack.back() : F.getDebugScope();
578-
auto *DS = new (SGM.M)
579-
SILDebugScope(Loc.getAsRegularLocation(), &getFunction(), Parent);
578+
auto *DS = Parent;
579+
// Don't nest a scope for Loc under Parent unless it's actually different.
580+
if (Parent->getLoc().getAsRegularLocation() != Loc.getAsRegularLocation())
581+
DS = DS = new (SGM.M)
582+
SILDebugScope(Loc.getAsRegularLocation(), &getFunction(), Parent);
580583
DebugScopeStack.push_back(DS);
581584
B.setCurrentDebugScope(DS);
582585
}

lib/SILGen/SILGenPattern.cpp

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,17 @@ void PatternMatchEmission::bindVariable(Pattern *pattern, VarDecl *var,
11931193

11941194
// Initialize the variable value.
11951195
InitializationPtr init = SGF.emitInitializationForVarDecl(var, immutable);
1196+
1197+
// Do not emit debug descriptions at this stage.
1198+
//
1199+
// If there are multiple let bindings, the value is forwarded to the case
1200+
// block via a phi. Emitting duplicate debug values for the incoming values
1201+
// leads to bogus debug info -- we must emit the debug value only on the phi.
1202+
//
1203+
// If there's only one let binding, we still want to wait until we can nest
1204+
// the scope for the case body under the scope for the pattern match.
1205+
init->setEmitDebugValueOnInit(false);
1206+
11961207
auto mv = value.getFinalManagedValue();
11971208
if (shouldTake(value, isIrrefutable)) {
11981209
mv.forwardInto(SGF, pattern, init.get());
@@ -2441,7 +2452,7 @@ void PatternMatchEmission::emitSharedCaseBlocks(
24412452
// the order of variables that are the incoming BB arguments. Setup the
24422453
// VarLocs to point to the incoming args and setup initialization so any
24432454
// args needing Cleanup will get that as well.
2444-
Scope scope(SGF.Cleanups, CleanupLocation(caseBlock));
2455+
LexicalScope scope(SGF, CleanupLocation(caseBlock));
24452456
unsigned argIndex = 0;
24462457
for (auto *vd : caseBlock->getCaseBodyVariables()) {
24472458
if (!vd->hasName())
@@ -2472,6 +2483,11 @@ void PatternMatchEmission::emitSharedCaseBlocks(
24722483
mv = SGF.emitManagedRValueWithCleanup(arg);
24732484
}
24742485

2486+
// Emit a debug description of the incoming arg, nested within the scope
2487+
// for the pattern match.
2488+
SILDebugVariable dbgVar(vd->isLet(), /*ArgNo=*/0);
2489+
SGF.B.emitDebugDescription(vd, mv.getValue(), dbgVar);
2490+
24752491
if (vd->isLet()) {
24762492
// Just emit a let and leave the cleanup alone.
24772493
SGF.VarLocs[vd].value = mv.getValue();
@@ -2608,6 +2624,10 @@ static void switchCaseStmtSuccessCallback(SILGenFunction &SGF,
26082624
// If we don't have a fallthrough or a multi-pattern 'case', we can emit the
26092625
// body inline. Emit the statement here and bail early.
26102626
if (!row.hasFallthroughTo() && caseBlock->getCaseLabelItems().size() == 1) {
2627+
// Debug values for case body variables must be nested within a scope for
2628+
// the case block to avoid name conflicts.
2629+
DebugScope scope(SGF, CleanupLocation(caseBlock));
2630+
26112631
// If we have case body vars, set them up to point at the matching var
26122632
// decls.
26132633
if (caseBlock->hasCaseBodyVariables()) {
@@ -2628,6 +2648,11 @@ static void switchCaseStmtSuccessCallback(SILGenFunction &SGF,
26282648
// Ok, we found a match. Update the VarLocs for the case block.
26292649
auto v = SGF.VarLocs[vd];
26302650
SGF.VarLocs[expected] = v;
2651+
2652+
// Emit a debug description for the variable, nested within a scope
2653+
// for the pattern match.
2654+
SILDebugVariable dbgVar(vd->isLet(), /*ArgNo=*/0);
2655+
SGF.B.emitDebugDescription(vd, v.value, dbgVar);
26312656
}
26322657
}
26332658
}
@@ -2747,7 +2772,7 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
27472772
emitProfilerIncrement(S);
27482773
JumpDest contDest(contBB, Cleanups.getCleanupsDepth(), CleanupLocation(S));
27492774

2750-
Scope switchScope(Cleanups, CleanupLocation(S));
2775+
LexicalScope switchScope(*this, CleanupLocation(S));
27512776

27522777
// Enter a break/continue scope. If we wanted a continue
27532778
// destination, it would probably be out here.

test/AutoDiff/SILOptimizer/activity_analysis.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -843,10 +843,10 @@ func testActiveEnumAddr<T>(_ e: IndirectEnum<T>) -> T {
843843
// CHECK: [ACTIVE] %6 = unchecked_take_enum_data_addr %3 : $*IndirectEnum<T>, #IndirectEnum.case1!enumelt
844844
// CHECK: [ACTIVE] %7 = alloc_stack $T, let, name "y1"
845845
// CHECK: bb2:
846-
// CHECK: [ACTIVE] %14 = unchecked_take_enum_data_addr %3 : $*IndirectEnum<T>, #IndirectEnum.case2!enumelt
847-
// CHECK: [ACTIVE] %15 = tuple_element_addr %14 : $*(Float, T), 0
848-
// CHECK: [VARIED] %16 = load [trivial] %15 : $*Float
849-
// CHECK: [ACTIVE] %17 = tuple_element_addr %14 : $*(Float, T), 1
850-
// CHECK: [ACTIVE] %18 = alloc_stack $T, let, name "y2"
846+
// CHECK: [ACTIVE] {{.*}} = unchecked_take_enum_data_addr {{.*}} : $*IndirectEnum<T>, #IndirectEnum.case2!enumelt
847+
// CHECK: [ACTIVE] {{.*}} = tuple_element_addr {{.*}} : $*(Float, T), 0
848+
// CHECK: [VARIED] {{.*}} = load [trivial] {{.*}} : $*Float
849+
// CHECK: [ACTIVE] {{.*}} = tuple_element_addr {{.*}} : $*(Float, T), 1
850+
// CHECK: [ACTIVE] {{.*}} = alloc_stack $T, let, name "y2"
851851
// CHECK: bb3:
852-
// CHECK: [NONE] %25 = tuple ()
852+
// CHECK: [NONE] {{.*}} = tuple ()

0 commit comments

Comments
 (0)