Skip to content

Commit 243e5a7

Browse files
authored
Merge pull request #3144 from adrian-prantl/26627376
2 parents 06abbb4 + bffda8a commit 243e5a7

File tree

6 files changed

+244
-18
lines changed

6 files changed

+244
-18
lines changed

lib/IRGen/IRGenDebugInfo.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -969,9 +969,7 @@ void IRGenDebugInfo::emitDbgIntrinsic(llvm::BasicBlock *BB,
969969
// the variable that is live throughout the function. With SIL
970970
// optimizations this is not guaranteed and a variable can end up in
971971
// two allocas (for example, one function inlined twice).
972-
if (!Opts.Optimize &&
973-
(isa<llvm::AllocaInst>(Storage) ||
974-
isa<llvm::UndefValue>(Storage)))
972+
if (isa<llvm::AllocaInst>(Storage))
975973
DBuilder.insertDeclare(Storage, Var, Expr, DL, BB);
976974
else
977975
DBuilder.insertDbgValueIntrinsic(Storage, 0, Var, Expr, DL, BB);

lib/IRGen/IRGenSIL.cpp

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "llvm/ADT/TinyPtrVector.h"
2727
#include "llvm/Support/Debug.h"
2828
#include "clang/AST/ASTContext.h"
29+
#include "clang/Basic/TargetInfo.h"
2930
#include "swift/Basic/Fallthrough.h"
3031
#include "swift/Basic/Range.h"
3132
#include "swift/Basic/STLExtras.h"
@@ -343,7 +344,10 @@ class IRGenSILFunction :
343344
StackSlotKey;
344345
/// Keeps track of the mapping of source variables to -O0 shadow copy allocas.
345346
llvm::SmallDenseMap<StackSlotKey, Address, 8> ShadowStackSlots;
347+
llvm::SmallDenseMap<llvm::Type *, Address, 8> DebugScratchpads;
346348
llvm::SmallDenseMap<Decl *, SmallString<4>, 8> AnonymousVariables;
349+
llvm::SmallVector<std::pair<DominancePoint, llvm::Instruction *>, 8>
350+
ValueVariables;
347351
unsigned NumAnonVars = 0;
348352
unsigned NumCondFails = 0;
349353

@@ -577,7 +581,44 @@ class IRGenSILFunction :
577581
return Name;
578582
}
579583

580-
/// At -O0, emit a shadow copy of an Address in an alloca, so the
584+
/// At -Onone, forcibly keep all LLVM values that are tracked by
585+
/// debug variables alive by inserting an empty inline assembler
586+
/// expression depending on the value in the blocks dominated by the
587+
/// value.
588+
void emitDebugVariableRangeExtension(const SILBasicBlock *CurBB) {
589+
if (IGM.IRGen.Opts.Optimize)
590+
return;
591+
for (auto &Variable : ValueVariables) {
592+
auto VarDominancePoint = Variable.first;
593+
llvm::Value *Storage = Variable.second;
594+
if (getActiveDominancePoint() == VarDominancePoint ||
595+
isActiveDominancePointDominatedBy(VarDominancePoint)) {
596+
llvm::Type *ArgTys;
597+
auto *Ty = Storage->getType()->getScalarType();
598+
// Pointers and Floats are expected to fit into a register.
599+
if (Ty->isPointerTy() || Ty->isFloatingPointTy())
600+
ArgTys = { Storage->getType() };
601+
else {
602+
// The storage is guaranteed to be no larger than the register width.
603+
// Extend the storage so it would fit into a register.
604+
llvm::Type *IntTy;
605+
switch (IGM.getClangASTContext().getTargetInfo().getRegisterWidth()) {
606+
case 64: IntTy = IGM.Int64Ty; break;
607+
case 32: IntTy = IGM.Int32Ty; break;
608+
default: llvm_unreachable("unsupported register width");
609+
}
610+
ArgTys = { IntTy };
611+
Storage = Builder.CreateZExtOrBitCast(Storage, IntTy);
612+
}
613+
// Emit an empty inline assembler expression depending on the register.
614+
auto *AsmFnTy = llvm::FunctionType::get(IGM.VoidTy, ArgTys, false);
615+
auto *InlineAsm = llvm::InlineAsm::get(AsmFnTy, "", "r", true);
616+
Builder.CreateCall(InlineAsm, Storage);
617+
}
618+
}
619+
}
620+
621+
/// At -Onone, emit a shadow copy of an Address in an alloca, so the
581622
/// register allocator doesn't elide the dbg.value intrinsic when
582623
/// register pressure is high. There is a trade-off to this: With
583624
/// shadow copies, we lose the precise lifetime.
@@ -589,8 +630,23 @@ class IRGenSILFunction :
589630
if (IGM.IRGen.Opts.Optimize || (ArgNo == 0) ||
590631
isa<llvm::AllocaInst>(Storage) ||
591632
isa<llvm::UndefValue>(Storage) ||
592-
Ty == IGM.RefCountedPtrTy) // No debug info is emitted for refcounts.
593-
return Storage;
633+
Ty == IGM.RefCountedPtrTy) { // No debug info is emitted for refcounts.
634+
// Account for bugs in LLVM.
635+
//
636+
// - The LLVM type legalizer currently doesn't update debug
637+
// intrinsics when a large value is split up into smaller
638+
// pieces. Note that this heuristic as a bit too conservative
639+
// on 32-bit targets as it will also fire for doubles.
640+
//
641+
// - CodeGen Prepare may drop dbg.values pointing to PHI instruction.
642+
if (IGM.DataLayout.getTypeSizeInBits(Storage->getType()) <=
643+
IGM.getClangASTContext().getTargetInfo().getRegisterWidth() &&
644+
!isa<llvm::PHINode>(Storage)) {
645+
if (auto *Value = dyn_cast<llvm::Instruction>(Storage))
646+
ValueVariables.push_back({getActiveDominancePoint(), Value});
647+
return Storage;
648+
}
649+
}
594650

595651
if (Align.isZero())
596652
Align = IGM.getPointerAlignment();
@@ -1516,6 +1572,8 @@ void IRGenSILFunction::visitSILBasicBlock(SILBasicBlock *BB) {
15161572
}
15171573
}
15181574
}
1575+
if (isa<TermInst>(&I))
1576+
emitDebugVariableRangeExtension(BB);
15191577
visit(&I);
15201578

15211579
assert(!EmissionNotes.count(&I) &&

test/DebugInfo/WeakCapture.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ func function() {
1010
let b = B()
1111

1212
// Ensure that the local b and its weak copy are distinct local variables.
13-
// CHECK: call void @llvm.dbg.value(metadata %C11WeakCapture1B*
14-
// CHECK-SAME: metadata [[B:.*]], metadata
15-
// CHECK: call void @llvm.dbg.value(metadata %swift.weak*
16-
// CHECK-NOT: metadata [[B]]
13+
// CHECK: call void @llvm.dbg.{{.*}}(metadata %C11WeakCapture1B*
14+
// CHECK-SAME: metadata [[B:.*]], metadata
15+
// CHECK: call void @llvm.dbg.{{.*}}(metadata %swift.weak*
16+
// CHECK-NOT: metadata [[B]]
1717
// CHECK: call
1818
A(handler: { [weak b] _ in
1919
if b != nil { }
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %target-swift-frontend %s -g -emit-ir -o - | FileCheck %s
2+
3+
func use<T>(_ x: T) {}
4+
5+
func getInt32() -> Int32 { return -1 }
6+
7+
public func rangeExtension(_ b: Bool) {
8+
// CHECK: define {{.*}}rangeExtension
9+
let i = getInt32()
10+
// CHECK: llvm.dbg.value(metadata i32 [[I:.*]], i64 0, metadata
11+
use(i)
12+
if b {
13+
let j = getInt32()
14+
// CHECK: llvm.dbg.value(metadata i32 [[J:.*]], i64 0, metadata
15+
use(j)
16+
// CHECK: asm sideeffect "", "r"
17+
// CHECK: {{(asm sideeffect "", "r".*)|(zext i32)}} [[J]]
18+
}
19+
let z = getInt32()
20+
use(z)
21+
// CHECK: llvm.dbg.value(metadata i32 [[Z:.*]], i64 0, metadata
22+
// CHECK: {{(asm sideeffect "", "r".*)|(zext i32)}} [[I]]
23+
// CHECK: asm sideeffect "", "r"
24+
}

test/DebugInfo/local-vars.swift.gyb

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// An end-to-end test to ensure local variables have debug info. This
2+
// test only verifies that the variables show up in the debug info at
3+
// all. There are other tests testing liveness and representation.
4+
5+
// RUN: %gyb %s -o %t.swift
6+
// RUN: %target-swift-frontend %t.swift -g -emit-ir -o - | FileCheck %t.swift
7+
// RUN: %target-swift-frontend %t.swift -g -c -o %t.o
8+
// RUN: llvm-dwarfdump --debug-dump=info %t.o \
9+
// RUN: | FileCheck %t.swift --check-prefix=DWARF
10+
11+
public class C {
12+
let member : Int
13+
init(_ i : Int) { member = i }
14+
func isZero() -> Boolean { return member == 0 }
15+
}
16+
17+
public struct S {
18+
let i : Int32 = -1
19+
let j : Int32 = 2
20+
}
21+
22+
func use<T>(_ x: T) {}
23+
func variable_use<T>(_ x: inout T) {}
24+
25+
% def derive_name((type, val)):
26+
% return (type.replace('<', '_').replace(' ', '_').replace(',', '_')
27+
% .replace('?', '_').replace('>', '_')
28+
% .replace('[', '_').replace(']', '_'), type, val)
29+
% for name, type, val in map(derive_name,
30+
% [("UInt64", "64"), ("UInt32", "32"), ("Int64", "64"), ("Int32", "32"),
31+
% ("Int", "42"), ("UInt", "42"), ("C", "C(42)"), ("String", '"string"'),
32+
% ("Dictionary<UInt64, String>", '[1:"entry"]'),
33+
% ("Float", "2.71"), ("Double", "3.14"), ("[UInt64]", "[1, 2, 3]"),
34+
% ("S", "S()")]):
35+
36+
public func constant_${name}() -> ${type} {
37+
let v : ${type} = ${val}
38+
// CHECK: !DILocalVariable(name: "v",{{.*}} line: [[@LINE-1]]
39+
// DWARF: DW_TAG_subprogram
40+
// DWARF: DW_AT_name {{.*}}constant_${name}
41+
// DWARF-NOT: DW_TAG_subprogram
42+
// DWARF: DW_TAG_variable
43+
// DWARF-NOT: DW_TAG
44+
// DWARF: {{(DW_AT_location)|(DW_AT_const_value)}}
45+
// DWARF-NOT: DW_TAG
46+
// DWARF: DW_AT_name {{.*}}"v"
47+
return v
48+
}
49+
50+
public func constvar_${name}() {
51+
var v : ${type} = ${val}
52+
// CHECK: !DILocalVariable(name: "v",{{.*}} line: [[@LINE-1]]
53+
// DWARF: DW_TAG_subprogram
54+
// DWARF: DW_AT_name {{.*}}constvar_${name}
55+
// DWARF-NOT: DW_TAG_subprogram
56+
// DWARF: DW_TAG_variable
57+
// DWARF-NOT: DW_TAG
58+
// DWARF: {{(DW_AT_location)|(DW_AT_const_value)}}
59+
// DWARF-NOT: DW_TAG
60+
// DWARF: DW_AT_name {{.*}}"v"
61+
variable_use(&v)
62+
}
63+
64+
public func let_${name}() {
65+
let v : ${type} = constant_${name}()
66+
// CHECK: !DILocalVariable(name: "v",{{.*}} line: [[@LINE-1]]
67+
// DWARF: DW_TAG_subprogram
68+
// DWARF: DW_AT_name {{.*}}let_${name}
69+
// DWARF-NOT: DW_TAG_subprogram
70+
// DWARF: DW_TAG_variable
71+
// DWARF-NOT: DW_TAG
72+
// DWARF: {{(DW_AT_location)|(DW_AT_const_value)}}
73+
// DWARF-NOT: DW_TAG
74+
// DWARF: DW_AT_name {{.*}}"v"
75+
use(v)
76+
}
77+
78+
public func optional_${name}() -> ${type}? {
79+
return constant_${name}();
80+
}
81+
82+
public func guard_let_${name}() {
83+
let opt : ${type}? = optional_${name}()
84+
// CHECK: !DILocalVariable(name: "opt",{{.*}} line: [[@LINE-1]]
85+
// DWARF: DW_TAG_subprogram
86+
// DWARF: DW_AT_name {{.*}}guard_let_${name}
87+
// DWARF-NOT: DW_TAG_subprogram
88+
// DWARF: DW_TAG_variable
89+
// DWARF-NOT: DW_TAG
90+
// DWARF: DW_AT_location
91+
// DWARF-NOT: DW_TAG
92+
// DWARF: DW_AT_name
93+
// DWARF-SAME: "{{(opt)|(val)}}"
94+
guard let val = opt else {
95+
use(opt)
96+
fatalError()
97+
}
98+
// DWARF-NOT: DW_TAG
99+
// DWARF: DW_TAG_variable
100+
// DWARF-NOT: DW_TAG
101+
// DWARF: DW_AT_location
102+
// DWARF-NOT: DW_TAG
103+
// DWARF: DW_AT_name
104+
// DWARF-SAME: "{{(opt)|(val)}}"
105+
use(val)
106+
}
107+
108+
public func var_${name}() {
109+
var v : ${type} = constant_${name}()
110+
// CHECK: !DILocalVariable(name: "v",{{.*}} line: [[@LINE-1]]
111+
// DWARF: DW_TAG_subprogram
112+
// DWARF: DW_AT_name {{.*}}var_${name}
113+
// DWARF-NOT: DW_TAG_subprogram
114+
// DWARF: DW_TAG_variable
115+
// DWARF-NOT: DW_TAG
116+
// DWARF: DW_AT_location
117+
// DWARF-NOT: DW_TAG
118+
// DWARF: DW_AT_name {{.*}}"v"
119+
variable_use(&v)
120+
}
121+
122+
public func arg_${name}(_ v: ${type}) {
123+
// CHECK: !DILocalVariable(name: "v",{{.*}} line: [[@LINE-1]]
124+
// DWARF: DW_TAG_subprogram
125+
// DWARF: DW_AT_name {{.*}}arg_${name}
126+
// DWARF-NOT: DW_TAG_subprogram
127+
// DWARF: DW_TAG_formal_parameter
128+
// DWARF-NOT: DW_TAG
129+
// DWARF: DW_AT_location
130+
// DWARF-NOT: DW_TAG
131+
// DWARF: DW_AT_name {{.*}}"v"
132+
use(v)
133+
}
134+
135+
public func arg_inout_${name}(_ v: inout ${type}) {
136+
// CHECK: !DILocalVariable(name: "v",{{.*}} line: [[@LINE-1]]
137+
// DWARF: DW_TAG_subprogram
138+
// DWARF: DW_AT_name {{.*}}arg_inout_${name}
139+
// DWARF-NOT: DW_TAG_subprogram
140+
// DWARF: DW_TAG_formal_parameter
141+
// DWARF-NOT: DW_TAG
142+
// DWARF: DW_AT_location
143+
// DWARF-NOT: DW_TAG
144+
// DWARF: DW_AT_name {{.*}}"v"
145+
variable_use(&v)
146+
}

test/DebugInfo/patternmatching.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ switch p {
2525
// Verify that the branch has a location >= the cleanup.
2626
// SIL-CHECK-NEXT: br{{.*}}line:[[@LINE-3]]:17:cleanup
2727
// CHECK-SCOPES: call {{.*}}markUsed
28-
// CHECK-SCOPES: call void @llvm.dbg.value({{.*}}metadata ![[X1:[0-9]+]]
29-
// CHECK-SCOPES-SAME: !dbg ![[X1LOC:[0-9]+]]
30-
// CHECK-SCOPES: call void @llvm.dbg.value
31-
// CHECK-SCOPES: call void @llvm.dbg.value({{.*}}metadata ![[X2:[0-9]+]]
32-
// CHECK-SCOPES-SAME: !dbg ![[X2LOC:[0-9]+]]
33-
// CHECK-SCOPES: call void @llvm.dbg.value
34-
// CHECK-SCOPES: call void @llvm.dbg.value({{.*}}metadata ![[X3:[0-9]+]]
35-
// CHECK-SCOPES-SAME: !dbg ![[X3LOC:[0-9]+]]
28+
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[X1:[0-9]+]],
29+
// CHECK-SCOPES-SAME: !dbg ![[X1LOC:[0-9]+]]
30+
// CHECK-SCOPES: call void @llvm.dbg
31+
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[X2:[0-9]+]],
32+
// CHECK-SCOPES-SAME: !dbg ![[X2LOC:[0-9]+]]
33+
// CHECK-SCOPES: call void @llvm.dbg
34+
// CHECK-SCOPES: call void @llvm.dbg{{.*}}metadata ![[X3:[0-9]+]],
35+
// CHECK-SCOPES-SAME: !dbg ![[X3LOC:[0-9]+]]
3636
// CHECK-SCOPES: !DILocalVariable(name: "x",
3737
case (let x, let y) where x == -y:
3838
// Verify that all variables end up in separate appropriate scopes.

0 commit comments

Comments
 (0)