Skip to content

Commit a1e02c1

Browse files
Merge pull request #72845 from Snowy1803/6.0-silsroa-salvage-debug-info
[6.0] [DebugInfo] Salvage debug info for allocations in SILSROA
2 parents 2007dfa + 6077cb6 commit a1e02c1

File tree

7 files changed

+129
-15
lines changed

7 files changed

+129
-15
lines changed

lib/SILOptimizer/Transforms/SILMem2Reg.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,12 @@ static void promoteDebugValueAddr(DebugValueInst *dvai, SILValue value,
349349
auto varInfo = *dvai->getVarInfo();
350350
if (isa<DebugValueInst>(dvai)) {
351351
auto &diExpr = varInfo.DIExpr;
352-
if (diExpr)
352+
// FIXME: There should always be a DIExpr starting with an op_deref here
353+
// The debug_value is attached to a pointer type, and those don't exist
354+
// in Swift, so they should always be dereferenced.
355+
// However, this rule is broken in a lot of spaces, so we have to leave
356+
// this check to recover from wrong info
357+
if (diExpr && diExpr.startsWithDeref())
353358
diExpr.eraseElement(diExpr.element_begin());
354359
}
355360

lib/SILOptimizer/Transforms/SILSROA.cpp

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -195,13 +195,14 @@ SROAMemoryUseAnalyzer::
195195
createAllocas(llvm::SmallVector<AllocStackInst *, 4> &NewAllocations) {
196196
SILBuilderWithScope B(AI);
197197
SILType Type = AI->getType().getObjectType();
198+
std::optional<SILDebugVariable> AIDebugVarInfo =
199+
SILDebugVariable::createFromAllocation(AI);
198200

199201
// Intentionally dropping the debug location.
200202
SILLocation Loc = RegularLocation::getAutoGeneratedLocation();
201203
if (TT) {
202204
for (unsigned EltNo : indices(TT->getElementTypes())) {
203-
std::optional<SILDebugVariable> NewDebugVarInfo =
204-
SILDebugVariable::createFromAllocation(AI);
205+
std::optional<SILDebugVariable> NewDebugVarInfo = AIDebugVarInfo;
205206
if (NewDebugVarInfo)
206207
NewDebugVarInfo->DIExpr.append(
207208
SILDebugInfoExpression::createTupleFragment(TT, EltNo));
@@ -215,8 +216,7 @@ createAllocas(llvm::SmallVector<AllocStackInst *, 4> &NewAllocations) {
215216
"this point.");
216217
SILModule &M = AI->getModule();
217218
for (VarDecl *VD : SD->getStoredProperties()) {
218-
std::optional<SILDebugVariable> NewDebugVarInfo =
219-
SILDebugVariable::createFromAllocation(AI);
219+
std::optional<SILDebugVariable> NewDebugVarInfo = AIDebugVarInfo;
220220
if (NewDebugVarInfo)
221221
NewDebugVarInfo->DIExpr.append(
222222
SILDebugInfoExpression::createFragment(VD));
@@ -225,6 +225,10 @@ createAllocas(llvm::SmallVector<AllocStackInst *, 4> &NewAllocations) {
225225
NewDebugVarInfo, AI->hasDynamicLifetime(), AI->isLexical()));
226226
}
227227
}
228+
if (AIDebugVarInfo && NewAllocations.empty()) {
229+
// Don't eliminate empty structs, we can use undef as there is no data
230+
B.createDebugValue(Loc, SILUndef::get(AI), *AIDebugVarInfo);
231+
}
228232
}
229233

230234
void SROAMemoryUseAnalyzer::chopUpAlloca(std::vector<AllocStackInst *> &Worklist) {
@@ -274,7 +278,7 @@ void SROAMemoryUseAnalyzer::chopUpAlloca(std::vector<AllocStackInst *> &Worklist
274278
}
275279

276280
// Find all dealloc instructions for AI and then chop them up.
277-
llvm::SmallVector<DeallocStackInst *, 4> ToRemove;
281+
llvm::SmallVector<SILInstruction *, 4> ToRemove;
278282
for (auto *Operand : getNonDebugUses(SILValue(AI))) {
279283
SILInstruction *User = Operand->getUser();
280284
SILBuilderWithScope B(User);
@@ -290,12 +294,41 @@ void SROAMemoryUseAnalyzer::chopUpAlloca(std::vector<AllocStackInst *> &Worklist
290294
}
291295
}
292296

293-
// Remove the old DeallocStackInst instructions.
297+
for (auto *Operand : getDebugUses(SILValue(AI))) {
298+
SILInstruction *User = Operand->getUser();
299+
auto *DVI = dyn_cast<DebugValueInst>(User);
300+
assert(DVI && "getDebugUses should only return DebugValueInst");
301+
SILBuilderWithScope B(DVI);
302+
std::optional<SILDebugVariable> DVIVarInfo = DVI->getVarInfo();
303+
assert(DVIVarInfo && "debug_value without debug info");
304+
305+
for (size_t i : indices(NewAllocations)) {
306+
auto *NewAI = NewAllocations[i];
307+
SILDebugVariable VarInfo = *DVIVarInfo;
308+
if (TT) {
309+
VarInfo.DIExpr.append(
310+
SILDebugInfoExpression::createTupleFragment(TT, i));
311+
} else {
312+
VarInfo.DIExpr.append(
313+
SILDebugInfoExpression::createFragment(SD->getStoredProperties()[i]));
314+
}
315+
if (!VarInfo.Type)
316+
VarInfo.Type = AI->getElementType();
317+
B.createDebugValue(DVI->getLoc(), NewAI, VarInfo);
318+
}
319+
if (NewAllocations.empty()) {
320+
// Don't eliminate empty structs, we can use undef as there is no data
321+
B.createDebugValue(DVI->getLoc(), SILUndef::get(AI), *DVIVarInfo);
322+
}
323+
ToRemove.push_back(DVI);
324+
}
325+
326+
// Remove the old DeallocStackInst/DebugValueInst instructions.
294327
for (auto *DSI : ToRemove) {
295328
DSI->eraseFromParent();
296329
}
297330

298-
eraseFromParentWithDebugInsts(AI);
331+
AI->eraseFromParent();
299332
}
300333

301334
/// Returns true, if values of \ty should be ignored, because \p ty is known

test/DebugInfo/sroa_debug_value.sil

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// RUN: %target-sil-opt -enable-sil-verify-all -sil-print-debuginfo -sroa %s | %FileCheck --check-prefix=CHECK-SROA %s
2+
sil_stage canonical
3+
4+
import Builtin
5+
import Swift
6+
7+
struct MyStruct {
8+
@_hasStorage var x: Int64 { get set }
9+
@_hasStorage var y: Int64 { get set }
10+
init(x: Int64, y: Int64)
11+
}
12+
13+
sil_scope 1 { loc "sroa.swift":2:8 parent @MyStructInit : $@convention(method) (Int64, Int64, @thin MyStruct.Type) -> MyStruct }
14+
15+
// MyStruct.init(x:y:)
16+
sil hidden @MyStructInit : $@convention(method) (Int64, Int64, @thin MyStruct.Type) -> MyStruct {
17+
bb0(%0 : $Int64, %1 : $Int64, %2 : $@thin MyStruct.Type):
18+
%3 = struct $MyStruct (%0 : $Int64, %1 : $Int64), loc "sroa.swift":2:8, scope 1
19+
return %3 : $MyStruct, loc "sroa.swift":2:8, scope 1
20+
} // end sil function 'MyStructInit'
21+
22+
sil_scope 2 { loc "sroa.swift":7:6 parent @foo : $@convention(thin) (Int64, Int64) -> Int64 }
23+
24+
// CHECK-SROA-LABEL: sil {{.+}} @foo
25+
// foo(in_x:in_y:)
26+
sil hidden @foo : $@convention(thin) (Int64, Int64) -> Int64 {
27+
bb0(%0 : $Int64, %1 : $Int64):
28+
%4 = alloc_stack $MyStruct, var, name "my_struct", loc "sroa.swift":8:9, scope 2
29+
debug_value %4 : $*MyStruct, let, name "my_copy", expr op_deref, loc "sroa.swift":7:10, scope 2
30+
// Make sure SROA propagate the debug info to the splitted alloc_stack/debug_value instructions
31+
// CHECK-SROA: %[[ALLOC_X:[0-9]+]] = alloc_stack $Int64, var
32+
// CHECK-SROA-SAME: (name "my_struct", loc "sroa.swift":8:9
33+
// CHECK-SROA-SAME: type $*MyStruct, expr op_fragment:#MyStruct.x
34+
// CHECK-SROA-SAME: loc * "<compiler-generated>":0:0
35+
// CHECK-SROA: %[[ALLOC_Y:[0-9]+]] = alloc_stack $Int64, var
36+
// CHECK-SROA-SAME: (name "my_struct", loc "sroa.swift":8:9
37+
// CHECK-SROA-SAME: type $*MyStruct, expr op_fragment:#MyStruct.y
38+
// CHECK-SROA-SAME: loc * "<compiler-generated>":0:0
39+
// CHECK-SROA: debug_value %[[ALLOC_X]] : $*Int64, let
40+
// CHECK-SROA-SAME: name "my_copy",
41+
// CHECK-SROA-SAME: type $MyStruct, expr op_deref:op_fragment:#MyStruct.x
42+
// CHECK-SROA-SAME: loc "sroa.swift":7:10
43+
// CHECK-SROA: debug_value %[[ALLOC_Y]] : $*Int64, let
44+
// CHECK-SROA-SAME: name "my_copy",
45+
// CHECK-SROA-SAME: type $MyStruct, expr op_deref:op_fragment:#MyStruct.y
46+
// CHECK-SROA-SAME: loc "sroa.swift":7:10
47+
%13 = struct_element_addr %4 : $*MyStruct, #MyStruct.x, loc "sroa.swift":9:17, scope 2
48+
store %0 to %13 : $*Int64, loc "sroa.swift":9:17, scope 2
49+
// CHECK-SROA: store %0 to %[[ALLOC_X]]
50+
%15 = struct_element_addr %4 : $*MyStruct, #MyStruct.y, loc "sroa.swift":10:17, scope 2
51+
store %1 to %15 : $*Int64, loc "sroa.swift":10:17, scope 2
52+
// CHECK-SROA: store %1 to %[[ALLOC_Y]]
53+
dealloc_stack %4 : $*MyStruct, loc "sroa.swift":8:9, scope 2
54+
return %0 : $Int64, loc "sroa.swift":11:5, scope 2
55+
} // end sil function 'foo'
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %target-sil-opt -enable-sil-verify-all -sil-print-debuginfo -sroa %s | %FileCheck --check-prefix=CHECK-SROA %s
2+
sil_stage canonical
3+
4+
import Builtin
5+
import Swift
6+
7+
struct Empty {}
8+
9+
sil_scope 1 { loc "sroa.swift":7:6 parent @bar : $@convention(thin) (Int64, Int64) -> Int64 }
10+
11+
// CHECK-SROA-LABEL: sil {{.+}} @bar
12+
// bar(in_x:in_y:)
13+
sil hidden @bar : $@convention(thin) (Int64, Int64) -> Int64 {
14+
bb0(%0 : $Int64, %1 : $Int64):
15+
%4 = alloc_stack $Empty, var, name "my_struct", loc "sroa.swift":8:9, scope 1
16+
debug_value %4 : $*Empty, let, name "my_copy", expr op_deref, loc "sroa.swift":7:10, scope 1
17+
// Make sure SROA keeps the debug info
18+
// CHECK-SROA: debug_value undef : $*Empty, var
19+
// CHECK-SROA-SAME: name "my_struct",
20+
// CHECK-SROA-SAME: loc "sroa.swift":8:9
21+
// CHECK-SROA: debug_value undef : $*Empty, let
22+
// CHECK-SROA-SAME: name "my_copy",
23+
// CHECK-SROA-SAME: loc "sroa.swift":7:10
24+
%6 = struct $Empty (), loc "sroa.swift":9:8, scope 1
25+
store %6 to %4 : $*Empty, loc "sroa.swift":10:8, scope 1
26+
dealloc_stack %4 : $*Empty, loc "sroa.swift":8:9, scope 1
27+
return %0 : $Int64, loc "sroa.swift":11:5, scope 1
28+
} // end sil function 'foo'

test/SILOptimizer/sroa.sil

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ sil @use_int32 : $@convention(thin) (Builtin.Int32) -> ()
4343
sil @struct_with_scalar_fields : $@convention(thin) (S1) -> () {
4444
bb0(%0 : $S1):
4545
%1 = alloc_stack $S1
46-
debug_value %1 : $*S1 // should not prevent the optimization
47-
debug_value %1 : $*S1 // should not prevent the optimization
4846
store %0 to %1 : $*S1
4947
%2 = function_ref @use_int32 : $@convention(thin) (Builtin.Int32) -> ()
5048
%3 = struct_element_addr %1 : $*S1, #S1.y

test/SILOptimizer/sroa_lifetime.sil

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ sil [ossa] @use_int32 : $@convention(thin) (Builtin.Int32) -> ()
4646
sil [ossa] @struct_with_scalar_fields : $@convention(thin) (S1) -> () {
4747
bb0(%0 : $S1):
4848
%1 = alloc_stack [lexical] $S1
49-
debug_value %1 : $*S1 // should not prevent the optimization
50-
debug_value %1 : $*S1 // should not prevent the optimization
5149
store %0 to [trivial] %1 : $*S1
5250
%2 = function_ref @use_int32 : $@convention(thin) (Builtin.Int32) -> ()
5351
%3 = struct_element_addr %1 : $*S1, #S1.y

test/SILOptimizer/sroa_ossa.sil

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ sil [ossa] @use_int32 : $@convention(thin) (Builtin.Int32) -> ()
4646
sil [ossa] @struct_with_scalar_fields : $@convention(thin) (S1) -> () {
4747
bb0(%0 : $S1):
4848
%1 = alloc_stack $S1
49-
debug_value %1 : $*S1 // should not prevent the optimization
50-
debug_value %1 : $*S1 // should not prevent the optimization
5149
store %0 to [trivial] %1 : $*S1
5250
%2 = function_ref @use_int32 : $@convention(thin) (Builtin.Int32) -> ()
5351
%3 = struct_element_addr %1 : $*S1, #S1.y
@@ -483,7 +481,6 @@ sil [ossa] @use_obj : $@convention(thin) (@owned Obj) -> ()
483481
sil [ossa] @struct_with_obj_fields : $@convention(thin) (@owned NotTrivial) -> () {
484482
bb0(%0 : @owned $NotTrivial):
485483
%1 = alloc_stack $NotTrivial
486-
debug_value %1 : $*NotTrivial // should not prevent the optimization
487484
store %0 to [init] %1 : $*NotTrivial
488485
%2 = function_ref @use_obj : $@convention(thin) (@owned Obj) -> ()
489486
%3 = struct_element_addr %1 : $*NotTrivial, #NotTrivial.y

0 commit comments

Comments
 (0)