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:
Vedant Kumar
2020-06-10 13:31:10 -07:00
committed by GitHub
parent 9b77762f54
commit 60ec3f1b90
20 changed files with 227 additions and 83 deletions

View File

@@ -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, \

View File

@@ -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.

View File

@@ -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.

View File

@@ -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();

View File

@@ -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

View File

@@ -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;

View File

@@ -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,

View File

@@ -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);
}

View File

@@ -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.

View File

@@ -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 ()

View File

@@ -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]],
}

View File

@@ -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"

View File

@@ -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

View 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"))

View File

@@ -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")

View File

@@ -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]]

View File

@@ -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]+]]

View File

@@ -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]]

View File

@@ -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]]

View File

@@ -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)