Skip to content

Commit da5ef90

Browse files
committed
[DebugInfo] Prevent salvaged debug vars from being marked artificial
We were using compiler-generated source location (i.e. line number 0) on `debug_value` instructions were emitted by salvage debug info. But it turned out that IRGen will attach DW_AT_artificial on the associated debug variables, which are hidden in debugger by default. This patch prevent this issue by using the source location from the just-deleted instruction for these `debug_value` instructions. This indirectly triggered another bug where some of the LLVM !DIExpression-s we generated are actually invalid -- more specifically, the fragment inside is covering the whole debug variable. The reason it was not previously caught is because LLVM verifier skips any debug variable that is marked artificial, and all debug variables that have illegal fragment are falling under this category. Thus, this patch also fixes this issue by not generating the DW_OP_LLVM_fragment part if it's illegal.
1 parent c3390ec commit da5ef90

File tree

4 files changed

+44
-2
lines changed

4 files changed

+44
-2
lines changed

lib/IRGen/IRGenDebugInfo.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2542,6 +2542,23 @@ void IRGenDebugInfoImpl::emitDbgIntrinsic(
25422542
!llvm::FindDbgDeclareUses(Storage).empty())
25432543
return;
25442544

2545+
// Fragment DIExpression cannot cover the whole variable
2546+
// or going out-of-bound.
2547+
if (auto Fragment = Expr->getFragmentInfo())
2548+
if (auto VarSize = Var->getSizeInBits()) {
2549+
unsigned FragSize = Fragment->SizeInBits;
2550+
unsigned FragOffset = Fragment->OffsetInBits;
2551+
if (FragOffset + FragSize > *VarSize ||
2552+
FragSize == *VarSize) {
2553+
// Drop the fragment part
2554+
assert(Expr->isValid());
2555+
// Since this expression is valid, DW_OP_LLVM_fragment
2556+
// and its arguments must be the last 3 elements.
2557+
auto OrigElements = Expr->getElements();
2558+
Expr = DBuilder.createExpression(OrigElements.drop_back(3));
2559+
}
2560+
}
2561+
25452562
// A dbg.declare is only meaningful if there is a single alloca for
25462563
// the variable that is live throughout the function.
25472564
if (auto *Alloca = dyn_cast<llvm::AllocaInst>(Storage)) {

lib/SILOptimizer/Utils/InstOptUtils.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2119,8 +2119,7 @@ void swift::salvageDebugInfo(SILInstruction *I) {
21192119
DestVal.getDefiningInstruction())) {
21202120
if (auto VarInfo = ASI->getVarInfo())
21212121
SILBuilder(SI, ASI->getDebugScope())
2122-
.createDebugValue(RegularLocation::getAutoGeneratedLocation(),
2123-
SI->getSrc(), *VarInfo);
2122+
.createDebugValue(SI->getLoc(), SI->getSrc(), *VarInfo);
21242123
}
21252124
}
21262125
}

test/DebugInfo/debug_info_expression.sil

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ struct MyStruct {
88
var y: Builtin.Int64
99
}
1010

11+
struct SmallStruct {
12+
var z : Builtin.Int64
13+
}
14+
1115
sil_scope 1 { loc "file.swift":7:6 parent @test_fragment : $@convention(thin) () -> () }
1216

1317
// Testing op_fragment w/ debug_value_addr
@@ -16,6 +20,8 @@ bb0:
1620
%2 = alloc_stack $MyStruct, var, name "my_struct", loc "file.swift":8:9, scope 1
1721
// CHECK: %[[MY_STRUCT:.+]] = alloca %{{.*}}MyStruct
1822
// CHECK: llvm.dbg.declare(metadata {{.*}}* %[[MY_STRUCT]], metadata ![[VAR_DECL_MD:[0-9]+]]
23+
// CHECK: %[[SMALL_STRUCT:.+]] = alloca %{{.*}}SmallStruct
24+
// CHECK: llvm.dbg.declare(metadata {{.*}}* %[[SMALL_STRUCT]], metadata ![[SMALL_VAR_DECL_MD:[0-9]+]]
1925
%3 = struct_element_addr %2 : $*MyStruct, #MyStruct.x, loc "file.swift":9:17, scope 1
2026
// CHECK: %[[FIELD_X:.*]] = getelementptr {{.*}} %[[MY_STRUCT]]
2127
// CHECK-SIL: debug_value_addr %{{[0-9]+}} : $*Builtin.Int64
@@ -25,6 +31,17 @@ bb0:
2531
// CHECK: llvm.dbg.value(metadata {{.*}}* %[[FIELD_X]], metadata ![[VAR_DECL_MD]]
2632
// CHECK-SAME: !DIExpression(DW_OP_deref, DW_OP_LLVM_fragment, 0, 64)
2733
// CHECK-NOT: ), !dbg ![[VAR_DECL_MD]]
34+
35+
%4 = alloc_stack $SmallStruct, var, name "small_struct", loc "file.swift":10:11, scope 1
36+
%5 = struct_element_addr %4 : $*SmallStruct, #SmallStruct.z, loc "file.swift":11:13, scope 1
37+
// CHECK: %[[FIELD_Z:.*]] = getelementptr {{.*}} %[[SMALL_STRUCT]]
38+
// If the fragment covers the whole struct, we're not generating the
39+
// DW_OP_LLVM_fragment part.
40+
// CHECK: llvm.dbg.value(metadata {{.*}}* %[[FIELD_Z]], metadata ![[SMALL_VAR_DECL_MD]]
41+
// CHECK-SAME: !DIExpression(DW_OP_deref)
42+
debug_value_addr %5 : $*Builtin.Int64, var, (name "small_struct", loc "file.swift":10:11, scope 1), type $SmallStruct, expr op_fragment:#SmallStruct.z, loc "file.swift":11:13, scope 1
43+
dealloc_stack %4 : $*SmallStruct
44+
2845
dealloc_stack %2 : $*MyStruct
2946
%r = tuple()
3047
return %r : $()

test/DebugInfo/sroa_mem2reg.sil

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// RUN: %target-sil-opt -enable-sil-verify-all -sil-print-debuginfo -sroa -mem2reg %s -o %t.sil
33
// RUN: %FileCheck --check-prefix=CHECK-MEM2REG %s --input-file=%t.sil
44
// RUN: %target-swiftc_driver -Xfrontend -disable-debugger-shadow-copies -g -emit-ir %t.sil | %FileCheck --check-prefix=CHECK-IR %s
5+
// RUN: %target-swiftc_driver -Xfrontend -disable-debugger-shadow-copies -g -c %t.sil -o %t.o
6+
// RUN: %llvm-dwarfdump --debug-info %t.o | %FileCheck --check-prefix=CHECK-DWARF %s
57
sil_stage canonical
68

79
import Builtin
@@ -78,3 +80,10 @@ bb0(%0 : $Int64, %1 : $Int64):
7880
// CHECK-IR-SAME: line: 7
7981
// CHECK-IR: ![[ARG2_MD]] = !DILocalVariable(name: "in_y", arg: 2
8082
// CHECK-IR-SAME: line: 7
83+
84+
// CHECK-DWARF-LABEL: DW_AT_name ("foo")
85+
// CHECK-DWARF: DW_TAG_variable
86+
// CHECK-DWARF: DW_AT_name ("my_struct")
87+
// Shouldn't be marked artificial
88+
// CHECK-DWARF-NOT: DW_AT_artificial (true)
89+
// CHECK-DWARF: DW_TAG_{{.*}}

0 commit comments

Comments
 (0)