Skip to content

Commit 34f0093

Browse files
committed
[DebugInfo] Merge fragments from SIL with fragments created in IRGen
SIL variables can be split by SILSROA into separate allocations, each having op_fragment expressions in debug_value (VarInfo.DIExpr). These allocations can be further split by IRGen (multiple values in Storage argument). These "nested" fragments refer to the same DI variable, so it is important to merge them for the LLVM IR DI expression. The compiler used to ignore fragment expressions from SIL when IRGen fragments were also present. This led to incorrect DI info generation, and for some cases even triggered assertions in LLVM X86 CodeGen: DwarfExpression.cpp:679: void llvm::DwarfExpression::addFragmentOffset(const llvm::DIExpression *): Assertion `FragmentOffset >= OffsetInBits && "overlapping or duplicate fragments"' failed. The patch fixes issue swiftlang#64642. The LIT test is a reduced reproducer from that issue.
1 parent 46e98c1 commit 34f0093

File tree

2 files changed

+114
-30
lines changed

2 files changed

+114
-30
lines changed

lib/IRGen/IRGenDebugInfo.cpp

Lines changed: 74 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,11 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
215215

216216
/// Return false if we fail to create the right DW_OP_LLVM_fragment operand.
217217
bool handleFragmentDIExpr(const SILDIExprOperand &CurDIExprOp,
218-
SmallVectorImpl<uint64_t> &Operands);
218+
std::pair<unsigned, unsigned> &Fragment);
219219
/// Return false if we fail to create the desired !DIExpression.
220220
bool buildDebugInfoExpression(const SILDebugVariable &VarInfo,
221-
SmallVectorImpl<uint64_t> &Operands);
221+
SmallVectorImpl<uint64_t> &Operands,
222+
std::pair<unsigned, unsigned> &Fragment);
222223

223224
/// Emit a dbg.declare at the current insertion point in Builder.
224225
void emitVariableDeclaration(IRBuilder &Builder,
@@ -2522,7 +2523,8 @@ void IRGenDebugInfoImpl::emitOutlinedFunction(IRBuilder &Builder,
25222523
}
25232524

25242525
bool IRGenDebugInfoImpl::handleFragmentDIExpr(
2525-
const SILDIExprOperand &CurDIExprOp, SmallVectorImpl<uint64_t> &Operands) {
2526+
const SILDIExprOperand &CurDIExprOp,
2527+
std::pair<unsigned, unsigned> &Fragment) {
25262528
assert(CurDIExprOp.getOperator() == SILDIExprOperator::Fragment);
25272529
// Expecting a VarDecl that points to a field in an struct
25282530
auto DIExprArgs = CurDIExprOp.args();
@@ -2554,23 +2556,31 @@ bool IRGenDebugInfoImpl::handleFragmentDIExpr(
25542556
uint64_t OffsetInBits =
25552557
Offset->getUniqueInteger().getLimitedValue() * SizeOfByte;
25562558

2557-
// Translate to LLVM dbg intrinsic operands
2558-
Operands.push_back(llvm::dwarf::DW_OP_LLVM_fragment);
2559-
Operands.push_back(OffsetInBits);
2560-
Operands.push_back(SizeInBits);
2559+
// Translate to DW_OP_LLVM_fragment operands
2560+
Fragment = std::make_pair(OffsetInBits, SizeInBits);
25612561

25622562
return true;
25632563
}
25642564

25652565
bool IRGenDebugInfoImpl::buildDebugInfoExpression(
2566-
const SILDebugVariable &VarInfo, SmallVectorImpl<uint64_t> &Operands) {
2566+
const SILDebugVariable &VarInfo, SmallVectorImpl<uint64_t> &Operands,
2567+
std::pair<unsigned, unsigned> &Fragment) {
25672568
assert(VarInfo.DIExpr && "SIL debug info expression not found");
25682569

2570+
#ifndef NDEBUG
2571+
bool HasFragment = VarInfo.DIExpr.hasFragment();
2572+
#endif
2573+
25692574
const auto &DIExpr = VarInfo.DIExpr;
25702575
for (const SILDIExprOperand &ExprOperand : DIExpr.operands()) {
25712576
switch (ExprOperand.getOperator()) {
25722577
case SILDIExprOperator::Fragment:
2573-
if (!handleFragmentDIExpr(ExprOperand, Operands))
2578+
assert(HasFragment && "Fragment must be the last part of a DIExpr");
2579+
#ifndef NDEBUG
2580+
// Trigger the assert above if we have more than one fragment expression.
2581+
HasFragment = false;
2582+
#endif
2583+
if (!handleFragmentDIExpr(ExprOperand, Fragment))
25742584
return false;
25752585
break;
25762586
case SILDIExprOperator::Dereference:
@@ -2677,24 +2687,60 @@ void IRGenDebugInfoImpl::emitVariableDeclaration(
26772687
LocalVarCache.insert({Key, llvm::TrackingMDNodeRef(Var)});
26782688
}
26792689

2680-
auto appendDIExpression =
2681-
[&VarInfo, this](llvm::DIExpression *DIExpr) -> llvm::DIExpression * {
2682-
if (VarInfo.DIExpr) {
2683-
llvm::SmallVector<uint64_t, 2> Operands;
2684-
if (!buildDebugInfoExpression(VarInfo, Operands))
2685-
return nullptr;
2686-
if (Operands.size())
2687-
return llvm::DIExpression::append(DIExpr, Operands);
2688-
}
2689-
return DIExpr;
2690-
};
2691-
26922690
// Running variables for the current/previous piece.
26932691
bool IsPiece = Storage.size() > 1;
26942692
uint64_t SizeOfByte = CI.getTargetInfo().getCharWidth();
26952693
unsigned AlignInBits = SizeOfByte;
26962694
unsigned OffsetInBits = 0;
26972695
unsigned SizeInBits = 0;
2696+
std::pair<unsigned, unsigned> Fragment(0, 0);
2697+
2698+
auto appendDIExpression =
2699+
[&VarInfo, this](
2700+
llvm::DIExpression *DIExpr,
2701+
std::pair<unsigned, unsigned> PieceFragment) -> llvm::DIExpression * {
2702+
unsigned PieceFragmentOffset = PieceFragment.first;
2703+
unsigned PieceFragmentSize = PieceFragment.second;
2704+
2705+
if (!VarInfo.DIExpr) {
2706+
if (!PieceFragmentSize)
2707+
return DIExpr;
2708+
2709+
return llvm::DIExpression::createFragmentExpression(
2710+
DIExpr, PieceFragmentOffset, PieceFragmentSize)
2711+
.getValueOr(nullptr);
2712+
}
2713+
2714+
llvm::SmallVector<uint64_t, 2> Operands;
2715+
std::pair<unsigned, unsigned> VarFragment(0, 0);
2716+
if (!buildDebugInfoExpression(VarInfo, Operands, VarFragment))
2717+
return nullptr;
2718+
2719+
if (!Operands.empty())
2720+
DIExpr = llvm::DIExpression::append(DIExpr, Operands);
2721+
2722+
unsigned VarFragmentOffset = VarFragment.first;
2723+
unsigned VarFragmentSize = VarFragment.second;
2724+
2725+
// Add the fragment of the SIL variable.
2726+
if (VarFragmentSize)
2727+
DIExpr = llvm::DIExpression::createFragmentExpression(
2728+
DIExpr, VarFragmentOffset, VarFragmentSize)
2729+
.getValueOr(nullptr);
2730+
2731+
if (!DIExpr)
2732+
return nullptr;
2733+
2734+
// When the fragment of the SIL variable is further split into other
2735+
// fragments (PieceFragment), merge them into one DW_OP_LLVM_Fragment
2736+
// expression.
2737+
if (PieceFragmentSize)
2738+
return llvm::DIExpression::createFragmentExpression(
2739+
DIExpr, PieceFragmentOffset, PieceFragmentSize)
2740+
.getValueOr(nullptr);
2741+
2742+
return DIExpr;
2743+
};
26982744

26992745
for (llvm::Value *Piece : Storage) {
27002746
SmallVector<uint64_t, 3> Operands;
@@ -2723,16 +2769,12 @@ void IRGenDebugInfoImpl::emitVariableDeclaration(
27232769
}
27242770
#endif
27252771

2726-
// Add the piece DWARF expression.
2727-
Operands.push_back(llvm::dwarf::DW_OP_LLVM_fragment);
2728-
Operands.push_back(OffsetInBits);
2729-
Operands.push_back(SizeInBits);
2772+
// Add the piece DW_OP_LLVM_fragment operands
2773+
Fragment.first = OffsetInBits;
2774+
Fragment.second = SizeInBits;
27302775
}
27312776
llvm::DIExpression *DIExpr = DBuilder.createExpression(Operands);
2732-
// DW_OP_LLVM_fragment must be the last part of an DIExpr
2733-
// so we can't append more if IsPiece is true.
2734-
if (!IsPiece)
2735-
DIExpr = appendDIExpression(DIExpr);
2777+
DIExpr = appendDIExpression(DIExpr, Fragment);
27362778
if (DIExpr)
27372779
emitDbgIntrinsic(
27382780
Builder, Piece, Var, DIExpr, DInstLine, DInstLoc.column, Scope, DS,
@@ -2742,7 +2784,9 @@ void IRGenDebugInfoImpl::emitVariableDeclaration(
27422784

27432785
// Emit locationless intrinsic for variables that were optimized away.
27442786
if (Storage.empty()) {
2745-
if (auto *DIExpr = appendDIExpression(DBuilder.createExpression()))
2787+
std::pair<unsigned, unsigned> NoFragment(0, 0);
2788+
if (auto *DIExpr =
2789+
appendDIExpression(DBuilder.createExpression(), NoFragment))
27462790
emitDbgIntrinsic(Builder, llvm::ConstantInt::get(IGM.Int64Ty, 0), Var,
27472791
DIExpr, DInstLine, DInstLoc.column, Scope, DS,
27482792
Indirection == CoroDirectValue ||

test/IRGen/debug_fragment_merge.swift

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: %target-swift-frontend -primary-file %s -emit-sil -O -g | %FileCheck %s --check-prefix CHECK-SIL
2+
// RUN: %target-swift-frontend -primary-file %s -emit-ir -disable-llvm-optzns -O -g | %FileCheck %s
3+
4+
protocol External {
5+
func use(str: String);
6+
func decode<T>(_: T.Type) -> T
7+
}
8+
9+
struct Data {
10+
var a: String
11+
var b: String
12+
}
13+
14+
func test(cond: Int, external: External) async {
15+
// CHECK-DAG: ![[VAR:[0-9]+]] = !DILocalVariable(name: "data", scope: !{{[0-9]+}}, file: !{{[0-9]+}}, line: [[# @LINE + 1]], type: !{{[0-9]+}})
16+
let data: Data
17+
switch cond {
18+
// CHECK-DAG: ![[LOC1:[0-9]+]] = !DILocation(line: [[# @LINE + 1]], column: 12, scope: !{{.*}})
19+
case 42: data = external.decode(Data.self)
20+
// CHECK-DAG: ![[LOC2:[0-9]+]] = !DILocation(line: [[# @LINE + 1]], column: 12, scope: !{{.*}})
21+
default: data = external.decode(Data.self)
22+
}
23+
external.use(str: data.a)
24+
external.use(str: data.b)
25+
}
26+
27+
// CHECK-SIL: debug_value %{{.*}} : $String, let, (name "data", {{.*}}), type $*Data, expr op_fragment:#Data.a
28+
// CHECK-SIL: debug_value %{{.*}} : $String, let, (name "data", {{.*}}), type $*Data, expr op_fragment:#Data.b
29+
// CHECK-SIL: debug_value %{{.*}} : $String, let, (name "data", {{.*}}), type $*Data, expr op_fragment:#Data.a
30+
// CHECK-SIL: debug_value %{{.*}} : $String, let, (name "data", {{.*}}), type $*Data, expr op_fragment:#Data.b
31+
32+
// CHECK-DAG: llvm.dbg.declare{{.*}} metadata ![[VAR:[0-9]+]], metadata !DIExpression(DW_OP_LLVM_fragment, 192, 64){{.*}} !dbg ![[LOC1]]
33+
// CHECK-DAG: llvm.dbg.declare{{.*}} metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 128, 64){{.*}} !dbg ![[LOC1]]
34+
// CHECK-DAG: llvm.dbg.declare{{.*}} metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64){{.*}} !dbg ![[LOC1]]
35+
// CHECK-DAG: llvm.dbg.declare{{.*}} metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64){{.*}} !dbg ![[LOC1]]
36+
//
37+
// CHECK-DAG: llvm.dbg.declare{{.*}} metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 192, 64){{.*}} !dbg ![[LOC2]]
38+
// CHECK-DAG: llvm.dbg.declare{{.*}} metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 128, 64){{.*}} !dbg ![[LOC2]]
39+
// CHECK-DAG: llvm.dbg.declare{{.*}} metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 64, 64){{.*}} !dbg ![[LOC2]]
40+
// CHECK-DAG: llvm.dbg.declare{{.*}} metadata ![[VAR]], metadata !DIExpression(DW_OP_LLVM_fragment, 0, 64){{.*}} !dbg ![[LOC2]]

0 commit comments

Comments
 (0)