Derive the SILDebugScope for a variable declaration from its owning ASTScope.

The previous code made the assumption that the ASTScope for a variable
declaration should be the one of the declaration's source location. That is not
necessarily the case, in some cases it should be an ancestor scope. This patch
introduces a map from ValueDecl -> ASTScope that is derived from querying each
ASTScope for its locals, which matches also what happens in name lookup.  This
patch also fixes the nesting of SILDebugScopes created for guard statement
bodies, which are incorrectly nested in the ASTScope hierarchy.

rdar://108940570
This commit is contained in:
Adrian Prantl
2023-05-08 18:20:01 -07:00
parent a0ec3f45cd
commit b74cdf19e8
13 changed files with 115 additions and 79 deletions

View File

@@ -32,6 +32,7 @@
#include "swift/AST/PropertyWrappers.h"
#include "swift/AST/SourceFile.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Defer.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILProfiler.h"
#include "swift/SIL/SILUndef.h"
@@ -201,7 +202,17 @@ const SILDebugScope *SILGenFunction::getScopeOrNull(SILLocation Loc,
SourceLoc SLoc = Loc.getSourceLoc();
if (!SF || LastSourceLoc == SLoc)
return nullptr;
return getOrCreateScope(SLoc);
// Prime VarDeclScopeMap.
auto Scope = getOrCreateScope(SLoc);
if (ForMetaInstruction)
if (ValueDecl *ValDecl = Loc.getAsASTNode<ValueDecl>()) {
// The source location of a VarDecl isn't necessarily in the same scope
// that the variable resides in for name lookup purposes.
auto ValueScope = VarDeclScopeMap.find(ValDecl);
if (ValueScope != VarDeclScopeMap.end())
return getOrCreateScope(ValueScope->second, F.getDebugScope());
}
return Scope;
}
const SILDebugScope *SILGenFunction::getOrCreateScope(SourceLoc SLoc) {
@@ -378,9 +389,14 @@ SILGenFunction::getOrCreateScope(const ast_scope::ASTScopeImpl *ASTScope,
if (It != ScopeMap.end())
return It->second;
LLVM_DEBUG( ASTScope->print(llvm::errs(), 0, false, false) );
LLVM_DEBUG(ASTScope->print(llvm::errs(), 0, false, false));
SILDebugScope *SILScope = nullptr;
auto cache = [&](const SILDebugScope *SILScope) {
ScopeMap.insert({{ASTScope, InlinedAt}, SILScope});
assert(SILScope->getParentFunction() == &F &&
"inlinedAt points to other function");
return SILScope;
};
// Decide whether to pick a parent scope instead.
if (ASTScope->ignoreInDebugInfo()) {
@@ -390,11 +406,37 @@ SILGenFunction::getOrCreateScope(const ast_scope::ASTScopeImpl *ASTScope,
return ParentScope->InlinedCallSite != InlinedAt ? FnScope : ParentScope;
}
// Collect all variable declarations in this scope.
struct Consumer : public namelookup::AbstractASTScopeDeclConsumer {
const ast_scope::ASTScopeImpl *ASTScope;
VarDeclScopeMapTy &VarDeclScopeMap;
Consumer(const ast_scope::ASTScopeImpl *ASTScope,
VarDeclScopeMapTy &VarDeclScopeMap)
: ASTScope(ASTScope), VarDeclScopeMap(VarDeclScopeMap) {}
bool consume(ArrayRef<ValueDecl *> values,
NullablePtr<DeclContext> baseDC) override {
for (auto &value : values) {
assert(VarDeclScopeMap.count(value) == 0 && "VarDecl appears twice");
VarDeclScopeMap.insert({value, ASTScope});
}
return false;
}
bool lookInMembers(const DeclContext *) const override { return false; }
#ifndef NDEBUG
void startingNextLookupStep() override {}
void finishingLookup(std::string) const override {}
bool isTargetLookup() const override { return false; }
#endif
};
Consumer consumer(ASTScope, VarDeclScopeMap);
ASTScope->lookupLocalsOrMembers(consumer);
// Collapse BraceStmtScopes whose parent is a .*BodyScope.
if (auto Parent = ASTScope->getParent().getPtrOrNull())
if (Parent->getSourceRangeOfThisASTNode() ==
ASTScope->getSourceRangeOfThisASTNode())
return getOrCreateScope(Parent, FnScope, InlinedAt);
return cache(getOrCreateScope(Parent, FnScope, InlinedAt));
// The calls to defer closures have cleanup source locations pointing to the
// defer. Reparent them into the current debug scope.
@@ -402,32 +444,30 @@ SILGenFunction::getOrCreateScope(const ast_scope::ASTScopeImpl *ASTScope,
while (AncestorScope && AncestorScope != FnASTScope &&
!ScopeMap.count({AncestorScope, InlinedAt})) {
if (auto *FD = dyn_cast_or_null<FuncDecl>(
AncestorScope->getDeclIfAny().getPtrOrNull())) {
AncestorScope->getDeclIfAny().getPtrOrNull())) {
if (cast<DeclContext>(FD) != FunctionDC)
return B.getCurrentDebugScope();
return cache(B.getCurrentDebugScope());
// This is this function's own scope.
// If this is the outermost BraceStmt scope, ignore it.
if (AncestorScope == ASTScope->getParent().getPtrOrNull())
return FnScope;
return cache(FnScope);
break;
}
AncestorScope = AncestorScope->getParent().getPtrOrNull();
};
// Create the scope and recursively its parents. getLookupParent implements a
// special case for GuardBlockStmt, which is nested incorrectly.
auto *ParentScope = ASTScope->getLookupParent().getPtrOrNull();
const SILDebugScope *Parent =
getOrCreateScope(ASTScope->getParent().getPtrOrNull(), FnScope, InlinedAt);
getOrCreateScope(ParentScope, FnScope, InlinedAt);
SourceLoc SLoc = ASTScope->getSourceRangeOfThisASTNode().Start;
RegularLocation Loc(SLoc);
SILScope = new (SGM.M)
auto *SILScope = new (SGM.M)
SILDebugScope(Loc, FnScope->getParentFunction(), Parent, InlinedAt);
ScopeMap.insert({{ASTScope, InlinedAt}, SILScope});
assert(SILScope->getParentFunction() == &F &&
"inlinedAt points to other function");
return SILScope;
return cache(SILScope);
}
void SILGenFunction::enterDebugScope(SILLocation Loc, bool isBindingScope) {

View File

@@ -366,6 +366,10 @@ public:
SourceLoc LastSourceLoc;
using ASTScopeTy = ast_scope::ASTScopeImpl;
const ASTScopeTy *FnASTScope = nullptr;
using VarDeclScopeMapTy =
llvm::SmallDenseMap<ValueDecl *, const ASTScopeTy *, 8>;
/// The ASTScope each variable declaration belongs to.
VarDeclScopeMapTy VarDeclScopeMap;
/// Caches one SILDebugScope for each ASTScope.
llvm::SmallDenseMap<std::pair<const ASTScopeTy *, const SILDebugScope *>,
const SILDebugScope *, 16>

View File

@@ -8,14 +8,15 @@ public func f(_ xs: [String?]) {
sink(x)
}
}
// CHECK: sil_scope [[F:[0-9]+]] { loc "{{.*}}":5:13 parent @$s1a1fyySaySSSgGF
// CHECK: sil_scope [[S0:[0-9]+]] { loc "{{.*}}":6:3 parent [[F]] }
// CHECK: sil_scope [[S1:[0-9]+]] { loc "{{.*}}":6:15 parent [[S0]] }
// CHECK: sil_scope [[S3:[0-9]+]] { loc "{{.*}}":7:9 parent [[S1]] }
// CHECK: sil_scope [[S4:[0-9]+]] { loc "{{.*}}":7:13 parent [[S3]] }
// CHECK: debug_value %[[X:.*]] : $Optional<String>, let, name "x", {{.*}}, scope [[S0]]
// CHECK: retain_value %[[X]] : $Optional<String>, {{.*}}, scope [[S4]]
// CHECK: debug_value %[[X1:[0-9]+]] : $String, let, name "x", {{.*}}, scope [[S3]]
// CHECK: release_value %[[X1]] : $String, {{.*}}, scope [[S3]]
// CHECK: release_value %[[X]] : $Optional<String>, {{.*}}, scope [[S3]]
// CHECK: sil_scope [[F:[0-9]+]] { loc "{{.*}}":5:13 parent @$s1a1fyySaySSSgGF
// CHECK: sil_scope [[S3:[0-9]+]] { loc "{{.*}}":6:3 parent [[F]] }
// CHECK: sil_scope [[S4:[0-9]+]] { loc "{{.*}}":6:15 parent [[S3]] }
// CHECK: sil_scope [[S5:[0-9]+]] { loc "{{.*}}":7:13 parent [[S4]] }
// CHECK: sil_scope [[S6:[0-9]+]] { loc "{{.*}}":7:9 parent [[S4]] }
// CHECK: debug_value %[[X:.*]] : $Optional<String>, let, name "x", {{.*}}, scope [[S3]]
// CHECK: retain_value %[[X]] : $Optional<String>, {{.*}}, scope [[S5]]
// CHECK: debug_value %[[X1:[0-9]+]] : $String, let, name "x", {{.*}}, scope [[S6]]
// CHECK: release_value %[[X1]] : $String, {{.*}}, scope [[S6]]
// CHECK: release_value %[[X]] : $Optional<String>, {{.*}}, scope [[S6]]

View File

@@ -7,13 +7,13 @@ func f(c: AnyObject??) {
guard let x = x, let x = x else {
// CHECK: sil_scope [[S3:[0-9]+]] { {{.*}} parent @{{.*}}1f
// CHECK: sil_scope [[S4:[0-9]+]] { {{.*}} parent [[S3]] }
// CHECK: sil_scope [[S5:[0-9]+]] { {{.*}} parent [[S4]] }
// CHECK: sil_scope [[S6:[0-9]+]] { loc "{{.*}}":7:3 parent [[S4]] }
// CHECK: sil_scope [[S5:[0-9]+]] { {{.*}} parent [[S3]] }
// CHECK: sil_scope [[S6:[0-9]+]] { loc "{{.*}}":7:3 parent [[S5]] }
// CHECK: sil_scope [[S7:[0-9]+]] { loc "{{.*}}":7:17 parent [[S6]] }
// CHECK: sil_scope [[S8:[0-9]+]] { loc "{{.*}}":7:28 parent [[S7]] }
// CHECK: debug_value %{{.*}} : $Optional<Optional<AnyObject>>, let, name "x"{{.*}} scope [[S4]]
// CHECK: debug_value %{{.*}} : $Optional<AnyObject>, let, name "x", {{.*}} scope [[S6]]
// CHECK: debug_value %{{.*}} : $AnyObject, let, name "x", {{.*}} scope [[S7]]
// CHECK: debug_value %{{.*}} : $Optional<Optional<AnyObject>>, let, name "x"{{.*}} scope [[S5]]
// CHECK: debug_value %{{.*}} : $Optional<AnyObject>, let, name "x", {{.*}} scope [[S7]]
// CHECK: debug_value %{{.*}} : $AnyObject, let, name "x", {{.*}} scope [[S8]]
fatalError()
}
// CHECK: function_ref {{.*3use.*}} scope [[S8]]

View File

@@ -18,7 +18,7 @@ public func f(x: String?) throws {
}
// CHECK: sil_scope [[S1:[0-9]+]] { {{.*}} parent @{{.*}}1f
// CHECK: sil_scope [[S2:[0-9]+]] { {{.*}} parent [[S1]] }
// CHECK: sil_scope [[S3:[0-9]+]] { {{.*}} parent [[S2]] }
// CHECK: sil_scope [[S3:[0-9]+]] { {{.*}} parent [[S1]] }
// CHECK: sil_scope [[S4:[0-9]+]] { {{.*}} parent [[S2]] }
// CHECK: alloc_stack {{.*}} $SomeObject, let, name "s", {{.*}} scope [[S4]]
guard let s = s else {

View File

@@ -7,12 +7,15 @@ public class S {
private var c = [Int : C?]()
public func f(_ i: Int) throws -> C {
guard let x = c[i], let x else {
// CHECK: sil_scope [[X1:[0-9]+]] { loc "{{.*}}":[[@LINE-1]]:5
// CHECK: sil_scope [[X2:[0-9]+]] { loc "{{.*}}":[[@LINE-2]]:29
// CHECK: debug_value {{.*}} : $Optional<C>, let, name "x", {{.*}}, scope [[X1]]
// CHECK: debug_value %29 : $C, let, name "x", {{.*}}, scope [[X2]]
// CHECK-NEXT: scope [[X2]]
// CHECK: sil_scope [[P:[0-9]+]] { loc "{{.*}}":[[@LINE-1]]:5
// CHECK: sil_scope [[X1:[0-9]+]] { loc "{{.*}}":[[@LINE-2]]:19 parent [[P]]
// CHECK: sil_scope [[X2:[0-9]+]] { loc "{{.*}}":[[@LINE-3]]:29 parent [[X1]]
// CHECK: sil_scope [[GUARD:[0-9]+]] { loc "{{.*}}":[[@LINE-4]]:36 parent [[P]]
// CHECK: debug_value {{.*}} : $Optional<C>, let, name "x", {{.*}}, scope [[X1]]
// CHECK: debug_value {{.*}} : $C, let, name "x", {{.*}}, scope [[X2]]
// CHECK-NEXT: scope [[X2]]
throw MyError()
// CHECK: function_ref {{.*}}MyError{{.*}}:[[@LINE-1]]:13, scope [[GUARD]]
}
return x
}

View File

@@ -0,0 +1,13 @@
// RUN: %target-swift-frontend -g -emit-sil %s -parse-as-library -module-name a | %FileCheck %s
func use<T>(_ t: T) {}
public func f(value: String?) {
// CHECK: sil_scope [[S0:[0-9]+]] { loc "{{.*}}":[[@LINE-1]]:13
if let value, let value = Int(value) {
// CHECK: sil_scope [[S1:[0-9]+]] { loc "{{.*}}":[[@LINE-1]]:10
// CHECK: sil_scope [[S2:[0-9]+]] { loc "{{.*}}":[[@LINE-2]]:29 parent [[S1]] }
// CHECK: debug_value {{.*}} : $Optional<String>, let, name "value", {{.*}}, scope [[S0]]
// CHECK: debug_value {{.*}} : $String, let, name "value", {{.*}}, scope [[S1]]
// CHECK: debug_value {{.*}} : $Int, let, name "value", {{.*}}, scope [[S2]]
use((value))
}
}

View File

@@ -27,10 +27,9 @@ func inlined(_ x: Int64) -> Int64 {
let result = transparent(x)
// CHECK-DAG: ![[CALL]] = !DILocation(line: [[@LINE-1]], column: {{.*}}, scope: ![[INLINED1:.*]], inlinedAt: ![[INLINEDAT:.*]])
// CHECK-DAG: ![[INLINEDAT]] = !DILocation({{.*}}scope: ![[INLINEDAT1:[0-9]+]]
// CHECK-DAG: ![[INLINED1]] = distinct !DILexicalBlock(scope: ![[INLINED2:[0-9]+]]
// CHECK-DAG: ![[INLINED2]] = distinct !DILexicalBlock(scope: ![[INLINED3:[0-9]+]]
// CHECK-DAG: ![[INLINED1]] = distinct !DILexicalBlock(scope: ![[INLINED:[0-9]+]]
// Check if the inlined and removed function still has the correct linkage name.
// CHECK-DAG: ![[INLINED3]] = distinct !DISubprogram(name: "inlined", linkageName: "$s4main7inlinedys5Int64VADF"
// CHECK-DAG: ![[INLINED]] = distinct !DISubprogram(name: "inlined", linkageName: "$s4main7inlinedys5Int64VADF"
// TRANSPARENT-CHECK-NOT: !DISubprogram(name: "transparent"
return result
}

View File

@@ -0,0 +1,8 @@
// RUN: %target-swift-frontend -g -emit-sil %s -parse-as-library -module-name a | %FileCheck %s
func use<T>(_ t: T) {}
public func f(value: (Int, Int)) {
let (x, y) = value
// CHECK: debug_value {{.*}}let, name "x", {{.*}}, scope [[LET:[0-9]+]]
// CHECK: debug_value {{.*}}let, name "y", {{.*}}, scope [[LET]]
use((x,y))
}

View File

@@ -1,32 +0,0 @@
// RUN: %target-swift-frontend -g -emit-ir %s | %FileCheck %s
class UIViewController {
}
class UISplitViewController : UIViewController {
var delegate : UIViewController?
}
class UIWindow {
var rootViewController: UIViewController?
}
class AppDelegate {
var window: UIWindow?
func application() -> Bool {
// CHECK-DAG: !DILexicalBlock({{.*}}line: [[@LINE+1]], column: 13
if true {
// Verify that all debug line table entries for the expression
// below are in the same scope.
//
// CHECK-DAG: !DILocalVariable(name: "splitViewController", scope: ![[S1:[0-9]+]]
// CHECK-DAG: ![[S2:[0-9]+]] = distinct !DILexicalBlock(scope: ![[S1]]
// CHECK-DAG: !DILocation(line: [[@LINE+3]], column: 11, scope: ![[S1]])
// CHECK-DAG: !DILocation(line: [[@LINE+2]], column: 44, scope: ![[S2]])
// CHECK-DAG: !DILocation(line: [[@LINE+1]], column: 65, scope: ![[S2]])
let splitViewController = self.window!.rootViewController as! UISplitViewController
}
return true
}
}

View File

@@ -17,5 +17,6 @@ public func f(i: Int) {
// CHECK: ![[S3]] = distinct !DILexicalBlock(scope: ![[S1]],
// SIL: sil_scope [[S1:[0-9]+]] { {{.*}} parent @$s4main1f1iySi_tF
// SIL: sil_scope [[S2:[0-9]+]] { {{.*}} parent [[S1]] }
// SIL: sil_scope [[S3:[0-9]+]] { {{.*}} parent [[S1]] }
// SIL: debug_value %0 : $Int, let, name "i", argno 1,{{.*}}, scope [[S1]]
// SIL: debug_value {{.*}} : $Array<Int>, let, name "i", {{.*}}, scope [[S2]]
// SIL: debug_value {{.*}} : $Array<Int>, let, name "i", {{.*}}, scope [[S3]]

View File

@@ -20,8 +20,7 @@ func multiStatementInference() -> Int {
// The closure intruduced by the macro expansion should not contain any inline
// locations, but instead point directly into the macro buffer.
// CHECK-SIL: sil_scope [[S0:[0-9]+]] { loc "@__swiftmacro_9MacroUser23multiStatementInferenceSiyF0cD0fMf_.swift":1:1 parent @$s9MacroUser23multiStatementInferenceSiyFSiyXEfU_
// CHECK-SIL: sil_scope [[S1:[0-9]+]] { loc "@__swiftmacro_9MacroUser23multiStatementInferenceSiyF0cD0fMf_.swift":2:7 parent [[S0]] }
// CHECK-SIL: sil_scope [[S2:[0-9]+]] { loc "@__swiftmacro_9MacroUser23multiStatementInferenceSiyF0cD0fMf_.swift":2:14 parent [[S1]] }
// CHECK-SIL: sil_scope [[S2:[0-9]+]] { loc "@__swiftmacro_9MacroUser23multiStatementInferenceSiyF0cD0fMf_.swift":2:14 parent [[S0]] }
// CHECK-SIL: sil {{.*}} @$s9MacroUser23multiStatementInferenceSiyFSiyXEfU_
// CHECK-SIL-NOT: return

View File

@@ -19,12 +19,12 @@
// CHECK: destroy_value [[BOX_COPY]] : ${ var Int }, loc {{.*}}:33:11, scope 4
// CHECK: [[CLOSURE:%[^,]+]] = partial_apply [callee_guaranteed] [[SPECIALIZED_F]]([[REGISTER_11]]) : $@convention(thin) (Int) -> Int, loc {{.*}}:33:11, scope 4
// CHECK: [[BORROW:%.*]] = begin_borrow [lexical] [[CLOSURE]]
// CHECK: debug_value [[BORROW]] : $@callee_guaranteed () -> Int, let, name "f", loc {{.*}}:33:7, scope 5
// CHECK: [[CLOSURE_COPY:%[^,]+]] = copy_value [[BORROW]] : $@callee_guaranteed () -> Int, loc {{.*}}:34:10, scope 5
// CHECK: debug_value [[BORROW]] : $@callee_guaranteed () -> Int, let, name "f", loc {{.*}}:33:7, scope 6
// CHECK: [[CLOSURE_COPY:%[^,]+]] = copy_value [[BORROW]] : $@callee_guaranteed () -> Int, loc {{.*}}:34:10, scope 6
// There used to be an end_borrow here. We leave an emptyline here to preserve line numbers.
// CHECK: destroy_value [[CLOSURE]] : $@callee_guaranteed () -> Int, loc {{.*}}:35:1, scope 5
// CHECK: destroy_value [[BOX]] : ${ var Int }, loc {{.*}}:35:1, scope 5
// CHECK: return [[CLOSURE_COPY]] : $@callee_guaranteed () -> Int, loc {{.*}}:34:3, scope 5
// CHECK: destroy_value [[CLOSURE]] : $@callee_guaranteed () -> Int, loc {{.*}}:35:1, scope 6
// CHECK: destroy_value [[BOX]] : ${ var Int }, loc {{.*}}:35:1, scope 6
// CHECK: return [[CLOSURE_COPY]] : $@callee_guaranteed () -> Int, loc {{.*}}:34:3, scope 6
// CHECK: }