mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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
This commit is contained in:
@@ -899,6 +899,14 @@ public:
|
||||
DebugValueAddrInst *createDebugValueAddr(SILLocation Loc, SILValue src,
|
||||
SILDebugVariable Var);
|
||||
|
||||
/// Create a debug_value_addr if \p src is an address; a debug_value if not.
|
||||
SILInstruction *emitDebugDescription(SILLocation Loc, SILValue src,
|
||||
SILDebugVariable Var) {
|
||||
if (src->getType().isAddress())
|
||||
return createDebugValueAddr(Loc, src, Var);
|
||||
return createDebugValue(Loc, src, Var);
|
||||
}
|
||||
|
||||
#define NEVER_LOADABLE_CHECKED_REF_STORAGE(Name, ...) \
|
||||
Load##Name##Inst *createLoad##Name(SILLocation Loc, \
|
||||
SILValue src, \
|
||||
|
||||
@@ -56,6 +56,8 @@ public:
|
||||
/// Create a scope for an artificial function.
|
||||
SILDebugScope(SILLocation Loc);
|
||||
|
||||
SILLocation getLoc() const { return Loc; }
|
||||
|
||||
/// Return the function this scope originated from before being inlined.
|
||||
SILFunction *getInlinedFunction() const;
|
||||
|
||||
@@ -64,12 +66,10 @@ public:
|
||||
/// into.
|
||||
SILFunction *getParentFunction() const;
|
||||
|
||||
#ifndef NDEBUG
|
||||
SWIFT_DEBUG_DUMPER(dump(SourceManager &SM,
|
||||
llvm::raw_ostream &OS = llvm::errs(),
|
||||
unsigned Indent = 0));
|
||||
SWIFT_DEBUG_DUMPER(dump(SILModule &Mod));
|
||||
#endif
|
||||
void print(SourceManager &SM, llvm::raw_ostream &OS = llvm::errs(),
|
||||
unsigned Indent = 0) const;
|
||||
|
||||
void print(SILModule &Mod) const;
|
||||
};
|
||||
|
||||
/// Determine whether an instruction may not have a SILDebugScope.
|
||||
|
||||
@@ -490,6 +490,8 @@ public:
|
||||
Loc.ASTNode.ForDebugger.getOpaqueValue() ==
|
||||
R.Loc.ASTNode.ForDebugger.getOpaqueValue();
|
||||
}
|
||||
|
||||
inline bool operator!=(const SILLocation &R) const { return !(*this == R); }
|
||||
};
|
||||
|
||||
/// Allowed on any instruction.
|
||||
|
||||
@@ -696,6 +696,29 @@ public:
|
||||
return !isa<llvm::Constant>(Storage);
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
/// Check if \p Val can be stored into \p Alloca, and emit some diagnostic
|
||||
/// info if it can't.
|
||||
bool canAllocaStoreValue(Address Alloca, llvm::Value *Val,
|
||||
SILDebugVariable VarInfo,
|
||||
const SILDebugScope *Scope) {
|
||||
bool canStore =
|
||||
cast<llvm::PointerType>(Alloca->getType())->getElementType() ==
|
||||
Val->getType();
|
||||
if (canStore)
|
||||
return true;
|
||||
llvm::errs() << "Invalid shadow copy:\n"
|
||||
<< " Value : " << *Val << "\n"
|
||||
<< " Alloca: " << *Alloca.getAddress() << "\n"
|
||||
<< "---\n"
|
||||
<< "Previous shadow copy into " << VarInfo.Name
|
||||
<< " in the same scope!\n"
|
||||
<< "Scope:\n";
|
||||
Scope->print(getSILModule());
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Unconditionally emit a stack shadow copy of an \c llvm::Value.
|
||||
llvm::Value *emitShadowCopy(llvm::Value *Storage, const SILDebugScope *Scope,
|
||||
SILDebugVariable VarInfo, llvm::Optional<Alignment> _Align) {
|
||||
@@ -706,7 +729,8 @@ public:
|
||||
if (!Alloca.isValid())
|
||||
Alloca = createAlloca(Storage->getType(), Align, VarInfo.Name + ".debug");
|
||||
zeroInit(cast<llvm::AllocaInst>(Alloca.getAddress()));
|
||||
|
||||
assert(canAllocaStoreValue(Alloca, Storage, VarInfo, Scope) &&
|
||||
"bad scope?");
|
||||
ArtificialLocation AutoRestore(Scope, IGM.DebugInfo.get(), Builder);
|
||||
Builder.CreateStore(Storage, Alloca.getAddress(), Align);
|
||||
return Alloca.getAddress();
|
||||
|
||||
@@ -3381,7 +3381,7 @@ void SILCoverageMap::dump() const {
|
||||
#pragma warning(disable : 4996)
|
||||
#endif
|
||||
|
||||
void SILDebugScope::dump(SourceManager &SM, llvm::raw_ostream &OS,
|
||||
void SILDebugScope::print(SourceManager &SM, llvm::raw_ostream &OS,
|
||||
unsigned Indent) const {
|
||||
OS << "{\n";
|
||||
OS.indent(Indent);
|
||||
@@ -3391,7 +3391,7 @@ void SILDebugScope::dump(SourceManager &SM, llvm::raw_ostream &OS,
|
||||
OS.indent(Indent + 2);
|
||||
OS << " parent: ";
|
||||
if (auto *P = Parent.dyn_cast<const SILDebugScope *>()) {
|
||||
P->dump(SM, OS, Indent + 2);
|
||||
P->print(SM, OS, Indent + 2);
|
||||
OS.indent(Indent + 2);
|
||||
}
|
||||
else if (auto *F = Parent.dyn_cast<SILFunction *>())
|
||||
@@ -3403,15 +3403,15 @@ void SILDebugScope::dump(SourceManager &SM, llvm::raw_ostream &OS,
|
||||
OS.indent(Indent + 2);
|
||||
if (auto *CS = InlinedCallSite) {
|
||||
OS << "inlinedCallSite: ";
|
||||
CS->dump(SM, OS, Indent + 2);
|
||||
CS->print(SM, OS, Indent + 2);
|
||||
OS.indent(Indent + 2);
|
||||
}
|
||||
OS << "}\n";
|
||||
}
|
||||
|
||||
void SILDebugScope::dump(SILModule &Mod) const {
|
||||
void SILDebugScope::print(SILModule &Mod) const {
|
||||
// We just use the default indent and llvm::errs().
|
||||
dump(Mod.getASTContext().SourceMgr);
|
||||
print(Mod.getASTContext().SourceMgr);
|
||||
}
|
||||
|
||||
#if SWIFT_COMPILER_IS_MSVC
|
||||
|
||||
@@ -148,6 +148,9 @@ public:
|
||||
ManagedValue explodedElement,
|
||||
bool isInit) = 0;
|
||||
|
||||
/// Whether to emit a debug value during initialization.
|
||||
void setEmitDebugValueOnInit(bool emit) { EmitDebugValueOnInit = emit; }
|
||||
|
||||
/// Perform post-initialization bookkeeping for this initialization.
|
||||
virtual void finishInitialization(SILGenFunction &SGF) {}
|
||||
|
||||
@@ -158,6 +161,9 @@ public:
|
||||
"uninitialized");
|
||||
}
|
||||
|
||||
protected:
|
||||
bool EmitDebugValueOnInit = true;
|
||||
|
||||
private:
|
||||
Initialization(const Initialization &) = delete;
|
||||
Initialization(Initialization &&) = delete;
|
||||
|
||||
@@ -555,14 +555,13 @@ public:
|
||||
SGF.VarLocs[vd] = SILGenFunction::VarLoc::get(value);
|
||||
|
||||
// Emit a debug_value[_addr] instruction to record the start of this value's
|
||||
// lifetime.
|
||||
// lifetime, if permitted to do so.
|
||||
if (!EmitDebugValueOnInit)
|
||||
return;
|
||||
SILLocation PrologueLoc(vd);
|
||||
PrologueLoc.markAsPrologue();
|
||||
SILDebugVariable DbgVar(vd->isLet(), /*ArgNo=*/0);
|
||||
if (address)
|
||||
SGF.B.createDebugValueAddr(PrologueLoc, value, DbgVar);
|
||||
else
|
||||
SGF.B.createDebugValue(PrologueLoc, value, DbgVar);
|
||||
SGF.B.emitDebugDescription(PrologueLoc, value, DbgVar);
|
||||
}
|
||||
|
||||
void copyOrInitValueInto(SILGenFunction &SGF, SILLocation loc,
|
||||
|
||||
@@ -324,7 +324,7 @@ public:
|
||||
std::vector<BreakContinueDest> BreakContinueDestStack;
|
||||
std::vector<PatternMatchContext*> SwitchStack;
|
||||
/// Keep track of our current nested scope.
|
||||
std::vector<SILDebugScope*> DebugScopeStack;
|
||||
std::vector<const SILDebugScope *> DebugScopeStack;
|
||||
|
||||
/// The cleanup depth and BB for when the operand of a
|
||||
/// BindOptionalExpr is a missing value.
|
||||
@@ -571,12 +571,15 @@ public:
|
||||
StringRef getMagicFilePathString(SourceLoc loc);
|
||||
StringRef getMagicFunctionString();
|
||||
|
||||
/// Push a new debug scope and set its parent pointer.
|
||||
/// Enter the debug scope for \p Loc, creating it if necessary.
|
||||
void enterDebugScope(SILLocation Loc) {
|
||||
auto *Parent =
|
||||
DebugScopeStack.size() ? DebugScopeStack.back() : F.getDebugScope();
|
||||
auto *DS = new (SGM.M)
|
||||
SILDebugScope(Loc.getAsRegularLocation(), &getFunction(), Parent);
|
||||
auto *DS = Parent;
|
||||
// Don't nest a scope for Loc under Parent unless it's actually different.
|
||||
if (Parent->getLoc().getAsRegularLocation() != Loc.getAsRegularLocation())
|
||||
DS = DS = new (SGM.M)
|
||||
SILDebugScope(Loc.getAsRegularLocation(), &getFunction(), Parent);
|
||||
DebugScopeStack.push_back(DS);
|
||||
B.setCurrentDebugScope(DS);
|
||||
}
|
||||
|
||||
@@ -1193,6 +1193,17 @@ void PatternMatchEmission::bindVariable(Pattern *pattern, VarDecl *var,
|
||||
|
||||
// Initialize the variable value.
|
||||
InitializationPtr init = SGF.emitInitializationForVarDecl(var, immutable);
|
||||
|
||||
// Do not emit debug descriptions at this stage.
|
||||
//
|
||||
// If there are multiple let bindings, the value is forwarded to the case
|
||||
// block via a phi. Emitting duplicate debug values for the incoming values
|
||||
// leads to bogus debug info -- we must emit the debug value only on the phi.
|
||||
//
|
||||
// If there's only one let binding, we still want to wait until we can nest
|
||||
// the scope for the case body under the scope for the pattern match.
|
||||
init->setEmitDebugValueOnInit(false);
|
||||
|
||||
auto mv = value.getFinalManagedValue();
|
||||
if (shouldTake(value, isIrrefutable)) {
|
||||
mv.forwardInto(SGF, pattern, init.get());
|
||||
@@ -2441,7 +2452,7 @@ void PatternMatchEmission::emitSharedCaseBlocks(
|
||||
// the order of variables that are the incoming BB arguments. Setup the
|
||||
// VarLocs to point to the incoming args and setup initialization so any
|
||||
// args needing Cleanup will get that as well.
|
||||
Scope scope(SGF.Cleanups, CleanupLocation(caseBlock));
|
||||
LexicalScope scope(SGF, CleanupLocation(caseBlock));
|
||||
unsigned argIndex = 0;
|
||||
for (auto *vd : caseBlock->getCaseBodyVariables()) {
|
||||
if (!vd->hasName())
|
||||
@@ -2472,6 +2483,11 @@ void PatternMatchEmission::emitSharedCaseBlocks(
|
||||
mv = SGF.emitManagedRValueWithCleanup(arg);
|
||||
}
|
||||
|
||||
// Emit a debug description of the incoming arg, nested within the scope
|
||||
// for the pattern match.
|
||||
SILDebugVariable dbgVar(vd->isLet(), /*ArgNo=*/0);
|
||||
SGF.B.emitDebugDescription(vd, mv.getValue(), dbgVar);
|
||||
|
||||
if (vd->isLet()) {
|
||||
// Just emit a let and leave the cleanup alone.
|
||||
SGF.VarLocs[vd].value = mv.getValue();
|
||||
@@ -2608,6 +2624,10 @@ static void switchCaseStmtSuccessCallback(SILGenFunction &SGF,
|
||||
// If we don't have a fallthrough or a multi-pattern 'case', we can emit the
|
||||
// body inline. Emit the statement here and bail early.
|
||||
if (!row.hasFallthroughTo() && caseBlock->getCaseLabelItems().size() == 1) {
|
||||
// Debug values for case body variables must be nested within a scope for
|
||||
// the case block to avoid name conflicts.
|
||||
DebugScope scope(SGF, CleanupLocation(caseBlock));
|
||||
|
||||
// If we have case body vars, set them up to point at the matching var
|
||||
// decls.
|
||||
if (caseBlock->hasCaseBodyVariables()) {
|
||||
@@ -2628,6 +2648,11 @@ static void switchCaseStmtSuccessCallback(SILGenFunction &SGF,
|
||||
// Ok, we found a match. Update the VarLocs for the case block.
|
||||
auto v = SGF.VarLocs[vd];
|
||||
SGF.VarLocs[expected] = v;
|
||||
|
||||
// Emit a debug description for the variable, nested within a scope
|
||||
// for the pattern match.
|
||||
SILDebugVariable dbgVar(vd->isLet(), /*ArgNo=*/0);
|
||||
SGF.B.emitDebugDescription(vd, v.value, dbgVar);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2747,7 +2772,7 @@ void SILGenFunction::emitSwitchStmt(SwitchStmt *S) {
|
||||
emitProfilerIncrement(S);
|
||||
JumpDest contDest(contBB, Cleanups.getCleanupsDepth(), CleanupLocation(S));
|
||||
|
||||
Scope switchScope(Cleanups, CleanupLocation(S));
|
||||
LexicalScope switchScope(*this, CleanupLocation(S));
|
||||
|
||||
// Enter a break/continue scope. If we wanted a continue
|
||||
// destination, it would probably be out here.
|
||||
|
||||
@@ -843,10 +843,10 @@ func testActiveEnumAddr<T>(_ e: IndirectEnum<T>) -> T {
|
||||
// CHECK: [ACTIVE] %6 = unchecked_take_enum_data_addr %3 : $*IndirectEnum<T>, #IndirectEnum.case1!enumelt
|
||||
// CHECK: [ACTIVE] %7 = alloc_stack $T, let, name "y1"
|
||||
// CHECK: bb2:
|
||||
// CHECK: [ACTIVE] %14 = unchecked_take_enum_data_addr %3 : $*IndirectEnum<T>, #IndirectEnum.case2!enumelt
|
||||
// CHECK: [ACTIVE] %15 = tuple_element_addr %14 : $*(Float, T), 0
|
||||
// CHECK: [VARIED] %16 = load [trivial] %15 : $*Float
|
||||
// CHECK: [ACTIVE] %17 = tuple_element_addr %14 : $*(Float, T), 1
|
||||
// CHECK: [ACTIVE] %18 = alloc_stack $T, let, name "y2"
|
||||
// CHECK: [ACTIVE] {{.*}} = unchecked_take_enum_data_addr {{.*}} : $*IndirectEnum<T>, #IndirectEnum.case2!enumelt
|
||||
// CHECK: [ACTIVE] {{.*}} = tuple_element_addr {{.*}} : $*(Float, T), 0
|
||||
// CHECK: [VARIED] {{.*}} = load [trivial] {{.*}} : $*Float
|
||||
// CHECK: [ACTIVE] {{.*}} = tuple_element_addr {{.*}} : $*(Float, T), 1
|
||||
// CHECK: [ACTIVE] {{.*}} = alloc_stack $T, let, name "y2"
|
||||
// CHECK: bb3:
|
||||
// CHECK: [NONE] %25 = tuple ()
|
||||
// CHECK: [NONE] {{.*}} = tuple ()
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
// RUN: %FileCheck --check-prefix=CHECK-SCOPES %s < %t.ll
|
||||
// RUN: %target-swift-frontend -emit-sil -emit-verbose-sil -primary-file %s -o - | %FileCheck %s --check-prefix=SIL-CHECK
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// This comment must be at line 10 for the test to work.
|
||||
func markUsed<T>(_ t: T) {}
|
||||
|
||||
// CHECK-SCOPES: define {{.*}}classifyPoint2
|
||||
@@ -12,7 +17,7 @@ func classifyPoint2(_ p: (Double, Double)) {
|
||||
return input; // return_same gets called in both where statements
|
||||
}
|
||||
|
||||
switch p {
|
||||
switch p {
|
||||
case (let x, let y) where
|
||||
// CHECK: call {{.*}}double {{.*}}return_same{{.*}}, !dbg ![[LOC1:.*]]
|
||||
// CHECK: br {{.*}}, label {{.*}}, label {{.*}}, !dbg ![[LOC2:.*]]
|
||||
@@ -25,40 +30,14 @@ switch p {
|
||||
// SIL-CHECK: dealloc_stack{{.*}}line:[[@LINE-1]]:17:cleanup
|
||||
// Verify that the branch has a location >= the cleanup.
|
||||
// SIL-CHECK-NEXT: br{{.*}}auto_gen
|
||||
// CHECK-SCOPES: call void @llvm.dbg
|
||||
// CHECK-SCOPES: call void @llvm.dbg
|
||||
// CHECK-SCOPES: call void @llvm.dbg
|
||||
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[X1:[0-9]+]],
|
||||
// CHECK-SCOPES-SAME: !dbg ![[X1LOC:[0-9]+]]
|
||||
// CHECK-SCOPES: call void @llvm.dbg
|
||||
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[X2:[0-9]+]],
|
||||
// CHECK-SCOPES-SAME: !dbg ![[X2LOC:[0-9]+]]
|
||||
// CHECK-SCOPES: call void @llvm.dbg
|
||||
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[X3:[0-9]+]],
|
||||
// CHECK-SCOPES-SAME: !dbg ![[X3LOC:[0-9]+]]
|
||||
case (let x, let y) where x == -y:
|
||||
// Verify that all variables end up in separate appropriate scopes.
|
||||
// CHECK-SCOPES: ![[X1]] = !DILocalVariable(name: "x", scope: ![[SCOPE1:[0-9]+]],
|
||||
// CHECK-SCOPES-SAME: line: [[@LINE-3]]
|
||||
// CHECK-SCOPES: ![[X1LOC]] = !DILocation(line: [[@LINE-4]], column: 15,
|
||||
// CHECK-SCOPES-SAME: scope: ![[SCOPE1]])
|
||||
// FIXME: ![[SCOPE1]] = distinct !DILexicalBlock({{.*}}line: [[@LINE-6]]
|
||||
markUsed(x)
|
||||
case (let x, let y) where x >= -10 && x < 10 && y >= -10 && y < 10:
|
||||
// CHECK-SCOPES: ![[X2]] = !DILocalVariable(name: "x", scope: ![[SCOPE2:[0-9]+]],
|
||||
// CHECK-SCOPES-SAME: line: [[@LINE-2]]
|
||||
// CHECK-SCOPES: ![[X2LOC]] = !DILocation(line: [[@LINE-3]], column: 15,
|
||||
// CHECK-SCOPES-SAME: scope: ![[SCOPE2]])
|
||||
markUsed(x)
|
||||
case (let x, let y):
|
||||
// CHECK-SCOPES: ![[X3]] = !DILocalVariable(name: "x", scope: ![[SCOPE3:[0-9]+]],
|
||||
// CHECK-SCOPES-SAME: line: [[@LINE-2]]
|
||||
// CHECK-SCOPES: ![[X3LOC]] = !DILocation(line: [[@LINE-3]], column: 15,
|
||||
// CHECK-SCOPES-SAME: scope: ![[SCOPE3]])
|
||||
markUsed(x)
|
||||
}
|
||||
|
||||
switch p {
|
||||
switch p {
|
||||
case (let x, let y) where x == 0:
|
||||
if y == 0 { markUsed(x) }
|
||||
else { markUsed(y) } // SIL-CHECK-NOT: br{{.*}}line:[[@LINE]]:31:cleanup
|
||||
@@ -66,7 +45,57 @@ switch p {
|
||||
if y == 0 { markUsed(x) }
|
||||
else { markUsed(y) }
|
||||
} // SIL-CHECK: br{{.*}}line:[[@LINE]]:5:cleanup
|
||||
}
|
||||
}
|
||||
|
||||
// Test the scopes for the switch at line 20.
|
||||
|
||||
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[P:[0-9]+]],
|
||||
|
||||
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[X1:[0-9]+]],
|
||||
// CHECK-SCOPES-SAME: !dbg ![[X1LOC:[0-9]+]]
|
||||
|
||||
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[Y1:[0-9]+]],
|
||||
// CHECK-SCOPES-SAME: !dbg ![[Y1LOC:[0-9]+]]
|
||||
|
||||
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[X2:[0-9]+]],
|
||||
// CHECK-SCOPES-SAME: !dbg ![[X2LOC:[0-9]+]]
|
||||
|
||||
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[Y2:[0-9]+]],
|
||||
// CHECK-SCOPES-SAME: !dbg ![[Y2LOC:[0-9]+]]
|
||||
|
||||
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[X3:[0-9]+]],
|
||||
// CHECK-SCOPES-SAME: !dbg ![[X3LOC:[0-9]+]]
|
||||
|
||||
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[Y3:[0-9]+]],
|
||||
// CHECK-SCOPES-SAME: !dbg ![[Y3LOC:[0-9]+]]
|
||||
|
||||
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[X4:[0-9]+]],
|
||||
// CHECK-SCOPES-SAME: !dbg ![[X4LOC:[0-9]+]]
|
||||
|
||||
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[Y4:[0-9]+]],
|
||||
// CHECK-SCOPES-SAME: !dbg ![[Y4LOC:[0-9]+]]
|
||||
|
||||
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[X5:[0-9]+]],
|
||||
// CHECK-SCOPES-SAME: !dbg ![[X5LOC:[0-9]+]]
|
||||
|
||||
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[Y5:[0-9]+]],
|
||||
// CHECK-SCOPES-SAME: !dbg ![[Y5LOC:[0-9]+]]
|
||||
|
||||
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[X6:[0-9]+]],
|
||||
// CHECK-SCOPES-SAME: !dbg ![[X6LOC:[0-9]+]]
|
||||
|
||||
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[Y6:[0-9]+]],
|
||||
// CHECK-SCOPES-SAME: !dbg ![[Y6LOC:[0-9]+]]
|
||||
|
||||
// Verify that variables end up in separate appropriate scopes.
|
||||
|
||||
// CHECK-SCOPES: ![[X1]] = {{.*}}name: "x", scope: ![[SCOPE1:[0-9]+]], {{.*}}line: 37
|
||||
// CHECK-SCOPES: ![[SCOPE1]] = distinct !DILexicalBlock(scope: ![[SWITCH1:[0-9]+]], {{.*}}line: 37
|
||||
// CHECK-SCOPES: ![[SWITCH1]] = distinct !DILexicalBlock({{.*}}, line: 20
|
||||
// CHECK-SCOPES: ![[X1LOC]] = {{.*}}line: 37
|
||||
|
||||
// CHECK-SCOPES: ![[Y1]] = {{.*}}name: "y", scope: ![[SCOPE1]], {{.*}}line: 37
|
||||
// CHECK-SCOPES: ![[Y1LOC]] = {{.*}}line: 37
|
||||
|
||||
// CHECK: !DILocation(line: [[@LINE+1]],
|
||||
}
|
||||
|
||||
@@ -30,29 +30,25 @@ public func mangle(s: [UnicodeScalar]) -> [UnicodeScalar] {
|
||||
}
|
||||
|
||||
// The patterns in the first case statement each define an anonymous variable,
|
||||
// which shares the storage with the expression in the switch statement. Make
|
||||
// sure we emit a dbg.value once per basic block.
|
||||
// which shares the storage with the expression in the switch statement.
|
||||
|
||||
// Do we care to expose these via lldb?
|
||||
|
||||
// CHECK: define {{.*}}@"$s11patternvars6mangle1sSayAA13UnicodeScalarVGAF_tFA2EXEfU_"
|
||||
// CHECK: %[[VAL:[0-9]+]] = call swiftcc i32 @"$s11patternvars13UnicodeScalarV5values6UInt32Vvg"(i32 %0)
|
||||
// CHECK-NEXT: call void @llvm.dbg.value(metadata i32 %[[VAL]]
|
||||
// CHECK: {{[0-9]+}}:
|
||||
// CHECK: call void @llvm.dbg.value(metadata i32 %[[VAL]]
|
||||
// CHECK-NOT: call void @llvm.dbg.value
|
||||
// CHECK-NOT: call void asm sideeffect "", "r"
|
||||
|
||||
// CHECK: {{[0-9]+}}:
|
||||
// CHECK: call void @llvm.dbg.value(metadata i32 %[[VAL]]
|
||||
// CHECK-NOT: call void @llvm.dbg.value
|
||||
// CHECK-NOT: call void asm sideeffect "", "r"
|
||||
|
||||
// CHECK: {{[0-9]+}}:
|
||||
// CHECK: call void @llvm.dbg.value(metadata i32 %[[VAL]]
|
||||
// CHECK-NOT: call void @llvm.dbg.value
|
||||
// CHECK-NOT: call void asm sideeffect "", "r"
|
||||
|
||||
// CHECK: {{[0-9]+}}:
|
||||
// CHECK: call void @llvm.dbg.value(metadata i32 %[[VAL]]
|
||||
// CHECK-NOT: call void @llvm.dbg.value
|
||||
// CHECK-NOT: call void asm sideeffect "", "r"
|
||||
|
||||
|
||||
@@ -107,7 +107,6 @@ func dont_return<T>(_ argument: T) throws -> T {
|
||||
|
||||
// Catch HomeworkError.CatAteIt.
|
||||
// CHECK: [[MATCH]]([[T0:%.*]] : @owned $Cat):
|
||||
// CHECK-NEXT: debug_value
|
||||
// CHECK-NEXT: [[BORROWED_T0:%.*]] = begin_borrow [[T0]]
|
||||
// CHECK-NEXT: [[T0_COPY:%.*]] = copy_value [[BORROWED_T0]]
|
||||
// CHECK-NEXT: end_borrow [[BORROWED_T0]]
|
||||
@@ -314,7 +313,6 @@ func all_together_now_four(_ flag: Bool) throws -> Cat? {
|
||||
|
||||
// Catch HomeworkError.CatAteIt.
|
||||
// CHECK: [[MATCH_ATE]]([[T0:%.*]] : @owned $Cat):
|
||||
// CHECK-NEXT: debug_value
|
||||
// CHECK-NEXT: [[T0_COPY:%.*]] = copy_value [[T0]]
|
||||
// CHECK-NEXT: destroy_value [[T0]]
|
||||
// CHECK-NEXT: dealloc_stack [[DEST_TEMP]]
|
||||
@@ -325,7 +323,6 @@ func all_together_now_four(_ flag: Bool) throws -> Cat? {
|
||||
|
||||
// Catch HomeworkError.CatHidIt.
|
||||
// CHECK: [[MATCH_HID]]([[T0:%.*]] : @owned $Cat):
|
||||
// CHECK-NEXT: debug_value
|
||||
// CHECK-NEXT: [[T0_COPY:%.*]] = copy_value [[T0]]
|
||||
// CHECK-NEXT: destroy_value [[T0]]
|
||||
// CHECK-NEXT: dealloc_stack [[DEST_TEMP]]
|
||||
@@ -335,6 +332,7 @@ func all_together_now_four(_ flag: Bool) throws -> Cat? {
|
||||
// CHECK-NEXT: br [[EXTRACT]]([[T0_COPY]] : $Cat)
|
||||
|
||||
// CHECK: [[EXTRACT]]([[CAT:%.*]] : @owned $Cat):
|
||||
// CHECK-NEXT: debug_value [[CAT]] : $Cat, let, name "theCat"
|
||||
// CHECK-NEXT: [[BORROWED_CAT:%.*]] = begin_borrow [[CAT]] : $Cat
|
||||
// CHECK-NEXT: [[COPIED_CAT:%.*]] = copy_value [[BORROWED_CAT]] : $Cat
|
||||
// CHECK-NEXT: end_borrow [[BORROWED_CAT]] : $Cat
|
||||
|
||||
57
test/SILGen/switch-case-debug-descriptions.swift
Normal file
57
test/SILGen/switch-case-debug-descriptions.swift
Normal file
@@ -0,0 +1,57 @@
|
||||
// RUN: %target-swift-emit-silgen -module-name switch %s -Xllvm -sil-print-debuginfo | %FileCheck %s -check-prefix=SCOPE
|
||||
|
||||
enum E {
|
||||
case one(String)
|
||||
case two(String)
|
||||
case three(Int)
|
||||
}
|
||||
|
||||
func test1(_ e: E) {
|
||||
switch e { // SCOPE: sil_scope [[test1_switch:[0-9]+]] {{.*}}:[[@LINE]]:3
|
||||
case .one(let payload), .two(let payload): // SCOPE-NEXT: sil_scope [[test1_case1:[0-9]+]] {{.*}}:[[@LINE]]:3 parent [[test1_switch]]
|
||||
print(payload) // SCOPE-NEXT: sil_scope {{.*}}:[[@LINE]]:5 parent [[test1_case1]]
|
||||
case .three(let payload): // SCOPE-NEXT: sil_scope [[test1_case2:[0-9]+]] {{.*}}:[[@LINE]]:3 parent [[test1_switch]]
|
||||
print(payload) // SCOPE-NEXT: sil_scope {{.*}}:[[@LINE]]:5 parent [[test1_case2]]
|
||||
}
|
||||
}
|
||||
|
||||
func test2(_ e: E) {
|
||||
switch e { // SCOPE: sil_scope [[test2_switch:[0-9]+]] {{.*}}:[[@LINE]]:3
|
||||
case .one(let x): // SCOPE-NEXT: sil_scope [[test2_case1:[0-9]+]] {{.*}}:[[@LINE]]:3 parent [[test2_switch]]
|
||||
print(x) // SCOPE-NEXT: sil_scope {{.*}}:[[@LINE]]:5 parent [[test2_case1]]
|
||||
case .two(let x): // SCOPE-NEXT: sil_scope [[test2_case2:[0-9]+]] {{.*}}:[[@LINE]]:3 parent [[test2_switch]]
|
||||
print(x) // SCOPE-NEXT: sil_scope {{.*}}:[[@LINE]]:5 parent [[test2_case2]]
|
||||
case .three(let x): // SCOPE-NEXT: sil_scope [[test2_case3:[0-9]+]] {{.*}}:[[@LINE]]:3 parent [[test2_switch]]
|
||||
print(x) // SCOPE-NEXT: sil_scope {{.*}}:[[@LINE]]:5 parent [[test2_case3]]
|
||||
}
|
||||
}
|
||||
|
||||
func test3(_ e: E) {
|
||||
switch e { // SCOPE: sil_scope [[test3_switch:[0-9]+]] {{.*}}:[[@LINE]]:3
|
||||
case .one: // SCOPE-NEXT: sil_scope [[test3_case1:[0-9]+]] {{.*}}:[[@LINE]]:3 parent [[test3_switch]]
|
||||
print(1) // SCOPE-NEXT: sil_scope {{.*}}:[[@LINE]]:5 parent [[test3_case1]]
|
||||
case .three(let x): // SCOPE-NEXT: sil_scope [[test3_case2:[0-9]+]] {{.*}}:[[@LINE]]:3 parent [[test3_switch]]
|
||||
print(x) // SCOPE-NEXT: sil_scope {{.*}}:[[@LINE]]:5 parent [[test3_case2]]
|
||||
default: // SCOPE-NEXT: sil_scope [[test3_case3:[0-9]+]] {{.*}}:[[@LINE]]:3 parent [[test3_switch]]
|
||||
print("error") // SCOPE-NEXT: sil_scope {{.*}}:[[@LINE]]:5 parent [[test3_case3]]
|
||||
}
|
||||
}
|
||||
|
||||
func test4(_ e: E) {
|
||||
switch e { // SCOPE: sil_scope [[test4_switch:[0-9]+]] {{.*}}:[[@LINE]]:3
|
||||
case .one(let x): // SCOPE-NEXT: sil_scope [[test4_case1:[0-9]+]] {{.*}}:[[@LINE]]:3 parent [[test4_switch]]
|
||||
print(x) // SCOPE-NEXT: sil_scope {{.*}}:[[@LINE]]:5 parent [[test4_case1]]
|
||||
fallthrough
|
||||
case .two(let x): // SCOPE-NEXT: sil_scope [[test4_case2:[0-9]+]] {{.*}}:[[@LINE]]:3 parent [[test4_switch]]
|
||||
print(x) // SCOPE-NEXT: sil_scope {{.*}}:[[@LINE]]:5 parent [[test4_case2]]
|
||||
fallthrough
|
||||
default:
|
||||
print("default") // SCOPE-NEXT: sil_scope [[test4_default:[0-9]+]] {{.*}}:[[@LINE]]:5 parent [[test4_switch]]
|
||||
// SCOPE: string_literal utf8 "default", {{.*}} scope [[test4_default]]
|
||||
}
|
||||
}
|
||||
|
||||
test1(E.one("payload1"))
|
||||
test2(E.two("payload2"))
|
||||
test3(E.three(3))
|
||||
test4(E.one("payload1"))
|
||||
@@ -1062,14 +1062,13 @@ func testMultiPatternsWithOuterScopeSameNamedVar(base: Int?, filter: Int?) {
|
||||
print("both: \(base), \(filter)")
|
||||
case (.some(let base), .none), (.none, .some(let base)):
|
||||
// CHECK: bb3:
|
||||
// CHECK-NEXT: debug_value %8 : $Int, let, name "base"
|
||||
// CHECK-NEXT: br bb6(%8 : $Int)
|
||||
|
||||
// CHECK: bb5([[OTHER_BASE:%.*]] : $Int)
|
||||
// CHECK-NEXT: debug_value [[OTHER_BASE]] : $Int, let, name "base"
|
||||
// CHECK-NEXT: br bb6([[OTHER_BASE]] : $Int)
|
||||
|
||||
// CHECK: bb6([[ARG:%.*]] : $Int):
|
||||
// CHECK-NEXT: debug_value [[ARG]] : $Int, let, name "base"
|
||||
print("single: \(base)")
|
||||
default:
|
||||
print("default")
|
||||
|
||||
@@ -19,14 +19,10 @@ func isOn(_ b: Binary) -> Bool {
|
||||
// CHECK-LABEL: sil hidden [ossa] @$s16switch_debuginfo5test11iySi_tF
|
||||
func test1(i: Int) {
|
||||
switch i {
|
||||
// CHECK-NOT: [[LOC]]:[[@LINE+1]]
|
||||
case 0: // CHECK: debug_value {{.*}} : $Int, let, name "$match", [[LOC]]:[[@LINE]]
|
||||
// CHECK-NOT: [[LOC]]:[[@LINE-1]]
|
||||
case 0: // CHECK-NOT: [[LOC]]:[[@LINE]]
|
||||
nop1()
|
||||
|
||||
// CHECK-NOT: [[LOC]]:[[@LINE+1]]
|
||||
case 1: // CHECK: debug_value {{.*}} : $Int, let, name "$match", [[LOC]]:[[@LINE]]
|
||||
// CHECK-NOT: [[LOC]]:[[@LINE-1]]
|
||||
|
||||
case 1: // CHECK-NOT: [[LOC]]:[[@LINE]]
|
||||
nop1()
|
||||
|
||||
default: // CHECK-NOT: [[LOC]]:[[@LINE]]
|
||||
|
||||
@@ -149,10 +149,10 @@ func test5() {
|
||||
case (foo(), let n):
|
||||
// CHECK: cond_br {{%.*}}, [[YES_SECOND_CONDITION:bb[0-9]+]], {{bb[0-9]+}}
|
||||
// CHECK: [[YES_SECOND_CONDITION]]:
|
||||
// CHECK: debug_value [[SECOND_N:%.*]] : $Int, let, name "n"
|
||||
// CHECK: br [[CASE2]]([[SECOND_N]] : $Int)
|
||||
// CHECK: br [[CASE2]]([[SECOND_N:%.*]] : $Int)
|
||||
|
||||
// CHECK: [[CASE2]]([[INCOMING_N:%.*]] : $Int):
|
||||
// CHECK: debug_value [[INCOMING_N]] : $Int, let, name "n"
|
||||
// CHECK: [[Z:%.*]] = function_ref @$s18switch_fallthrough1zyySiF
|
||||
// CHECK: apply [[Z]]([[INCOMING_N]]) : $@convention(thin) (Int) -> ()
|
||||
// CHECK: br [[CONT:bb[0-9]+]]
|
||||
|
||||
@@ -69,6 +69,8 @@ func guardFn(_ l: D, _ r: D) -> Bool { return true }
|
||||
// CHECK: cond_br {{%.*}}, [[GUARD_YES:bb[0-9]+]], [[GUARD_NO:bb[0-9]+]]
|
||||
//
|
||||
// CHECK: [[GUARD_YES]]:
|
||||
// CHECK-NEXT: debug_value [[R2]]
|
||||
// CHECK-NEXT: debug_value [[L2]]
|
||||
// CHECK-NEXT: destroy_value [[L2]]
|
||||
// CHECK-NEXT: destroy_value [[R2]]
|
||||
// CHECK-NEXT: end_borrow [[BORROWED_TUP]]
|
||||
|
||||
@@ -91,6 +91,7 @@ func multipleLabelsVar(e: E) {
|
||||
// CHECK-NEXT: br bb3
|
||||
|
||||
// CHECK: bb3:
|
||||
// CHECK-NEXT: debug_value_addr [[X_PHI]] : $*Any, var, name "x"
|
||||
// CHECK-NEXT: [[ANY_BOX:%.*]] = alloc_box ${ var Any }, var, name "x"
|
||||
// CHECK-NEXT: [[BOX_PAYLOAD:%.*]] = project_box [[ANY_BOX]] : ${ var Any }, 0
|
||||
// CHECK-NEXT: copy_addr [take] [[X_PHI]] to [initialization] [[BOX_PAYLOAD]]
|
||||
|
||||
@@ -537,14 +537,13 @@ func test_multiple_patterns1() {
|
||||
case (0, let x), (let x, 0):
|
||||
// CHECK: cond_br {{%.*}}, [[FIRST_MATCH_CASE:bb[0-9]+]], [[FIRST_FAIL:bb[0-9]+]]
|
||||
// CHECK: [[FIRST_MATCH_CASE]]:
|
||||
// CHECK: debug_value [[FIRST_X:%.*]] :
|
||||
// CHECK: br [[CASE_BODY:bb[0-9]+]]([[FIRST_X]] : $Int)
|
||||
// CHECK: br [[CASE_BODY:bb[0-9]+]]([[FIRST_X:%.*]] : $Int)
|
||||
// CHECK: [[FIRST_FAIL]]:
|
||||
// CHECK: cond_br {{%.*}}, [[SECOND_MATCH_CASE:bb[0-9]+]], [[SECOND_FAIL:bb[0-9]+]]
|
||||
// CHECK: [[SECOND_MATCH_CASE]]:
|
||||
// CHECK: debug_value [[SECOND_X:%.*]] :
|
||||
// CHECK: br [[CASE_BODY]]([[SECOND_X]] : $Int)
|
||||
// CHECK: br [[CASE_BODY]]([[SECOND_X:%.*]] : $Int)
|
||||
// CHECK: [[CASE_BODY]]([[BODY_VAR:%.*]] : $Int):
|
||||
// CHECK: debug_value [[BODY_VAR]] : $Int, let, name "x"
|
||||
// CHECK: [[A:%.*]] = function_ref @$s10switch_var1a1xySi_tF
|
||||
// CHECK: apply [[A]]([[BODY_VAR]])
|
||||
a(x: x)
|
||||
|
||||
Reference in New Issue
Block a user