mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[DebugInfo] Salvage debug info for allocations in SILSROA
This commit is contained in:
@@ -349,7 +349,12 @@ static void promoteDebugValueAddr(DebugValueInst *dvai, SILValue value,
|
||||
auto varInfo = *dvai->getVarInfo();
|
||||
if (isa<DebugValueInst>(dvai)) {
|
||||
auto &diExpr = varInfo.DIExpr;
|
||||
if (diExpr)
|
||||
// FIXME: There should always be a DIExpr starting with an op_deref here
|
||||
// The debug_value is attached to a pointer type, and those don't exist
|
||||
// in Swift, so they should always be dereferenced.
|
||||
// However, this rule is broken in a lot of spaces, so we have to leave
|
||||
// this check to recover from wrong info
|
||||
if (diExpr && diExpr.startsWithDeref())
|
||||
diExpr.eraseElement(diExpr.element_begin());
|
||||
}
|
||||
|
||||
|
||||
@@ -195,13 +195,14 @@ SROAMemoryUseAnalyzer::
|
||||
createAllocas(llvm::SmallVector<AllocStackInst *, 4> &NewAllocations) {
|
||||
SILBuilderWithScope B(AI);
|
||||
SILType Type = AI->getType().getObjectType();
|
||||
std::optional<SILDebugVariable> AIDebugVarInfo =
|
||||
SILDebugVariable::createFromAllocation(AI);
|
||||
|
||||
// Intentionally dropping the debug location.
|
||||
SILLocation Loc = RegularLocation::getAutoGeneratedLocation();
|
||||
if (TT) {
|
||||
for (unsigned EltNo : indices(TT->getElementTypes())) {
|
||||
std::optional<SILDebugVariable> NewDebugVarInfo =
|
||||
SILDebugVariable::createFromAllocation(AI);
|
||||
std::optional<SILDebugVariable> NewDebugVarInfo = AIDebugVarInfo;
|
||||
if (NewDebugVarInfo)
|
||||
NewDebugVarInfo->DIExpr.append(
|
||||
SILDebugInfoExpression::createTupleFragment(TT, EltNo));
|
||||
@@ -215,8 +216,7 @@ createAllocas(llvm::SmallVector<AllocStackInst *, 4> &NewAllocations) {
|
||||
"this point.");
|
||||
SILModule &M = AI->getModule();
|
||||
for (VarDecl *VD : SD->getStoredProperties()) {
|
||||
std::optional<SILDebugVariable> NewDebugVarInfo =
|
||||
SILDebugVariable::createFromAllocation(AI);
|
||||
std::optional<SILDebugVariable> NewDebugVarInfo = AIDebugVarInfo;
|
||||
if (NewDebugVarInfo)
|
||||
NewDebugVarInfo->DIExpr.append(
|
||||
SILDebugInfoExpression::createFragment(VD));
|
||||
@@ -225,6 +225,10 @@ createAllocas(llvm::SmallVector<AllocStackInst *, 4> &NewAllocations) {
|
||||
NewDebugVarInfo, AI->hasDynamicLifetime(), AI->isLexical()));
|
||||
}
|
||||
}
|
||||
if (AIDebugVarInfo && NewAllocations.empty()) {
|
||||
// Don't eliminate empty structs, we can use undef as there is no data
|
||||
B.createDebugValue(Loc, SILUndef::get(AI), *AIDebugVarInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void SROAMemoryUseAnalyzer::chopUpAlloca(std::vector<AllocStackInst *> &Worklist) {
|
||||
@@ -274,7 +278,7 @@ void SROAMemoryUseAnalyzer::chopUpAlloca(std::vector<AllocStackInst *> &Worklist
|
||||
}
|
||||
|
||||
// Find all dealloc instructions for AI and then chop them up.
|
||||
llvm::SmallVector<DeallocStackInst *, 4> ToRemove;
|
||||
llvm::SmallVector<SILInstruction *, 4> ToRemove;
|
||||
for (auto *Operand : getNonDebugUses(SILValue(AI))) {
|
||||
SILInstruction *User = Operand->getUser();
|
||||
SILBuilderWithScope B(User);
|
||||
@@ -290,12 +294,41 @@ void SROAMemoryUseAnalyzer::chopUpAlloca(std::vector<AllocStackInst *> &Worklist
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the old DeallocStackInst instructions.
|
||||
for (auto *Operand : getDebugUses(SILValue(AI))) {
|
||||
SILInstruction *User = Operand->getUser();
|
||||
auto *DVI = dyn_cast<DebugValueInst>(User);
|
||||
assert(DVI && "getDebugUses should only return DebugValueInst");
|
||||
SILBuilderWithScope B(DVI);
|
||||
std::optional<SILDebugVariable> DVIVarInfo = DVI->getVarInfo();
|
||||
assert(DVIVarInfo && "debug_value without debug info");
|
||||
|
||||
for (size_t i : indices(NewAllocations)) {
|
||||
auto *NewAI = NewAllocations[i];
|
||||
SILDebugVariable VarInfo = *DVIVarInfo;
|
||||
if (TT) {
|
||||
VarInfo.DIExpr.append(
|
||||
SILDebugInfoExpression::createTupleFragment(TT, i));
|
||||
} else {
|
||||
VarInfo.DIExpr.append(
|
||||
SILDebugInfoExpression::createFragment(SD->getStoredProperties()[i]));
|
||||
}
|
||||
if (!VarInfo.Type)
|
||||
VarInfo.Type = AI->getElementType();
|
||||
B.createDebugValue(DVI->getLoc(), NewAI, VarInfo);
|
||||
}
|
||||
if (NewAllocations.empty()) {
|
||||
// Don't eliminate empty structs, we can use undef as there is no data
|
||||
B.createDebugValue(DVI->getLoc(), SILUndef::get(AI), *DVIVarInfo);
|
||||
}
|
||||
ToRemove.push_back(DVI);
|
||||
}
|
||||
|
||||
// Remove the old DeallocStackInst/DebugValueInst instructions.
|
||||
for (auto *DSI : ToRemove) {
|
||||
DSI->eraseFromParent();
|
||||
}
|
||||
|
||||
eraseFromParentWithDebugInsts(AI);
|
||||
AI->eraseFromParent();
|
||||
}
|
||||
|
||||
/// Returns true, if values of \ty should be ignored, because \p ty is known
|
||||
|
||||
55
test/DebugInfo/sroa_debug_value.sil
Normal file
55
test/DebugInfo/sroa_debug_value.sil
Normal file
@@ -0,0 +1,55 @@
|
||||
// RUN: %target-sil-opt -enable-sil-verify-all -sil-print-debuginfo -sroa %s | %FileCheck --check-prefix=CHECK-SROA %s
|
||||
sil_stage canonical
|
||||
|
||||
import Builtin
|
||||
import Swift
|
||||
|
||||
struct MyStruct {
|
||||
@_hasStorage var x: Int64 { get set }
|
||||
@_hasStorage var y: Int64 { get set }
|
||||
init(x: Int64, y: Int64)
|
||||
}
|
||||
|
||||
sil_scope 1 { loc "sroa.swift":2:8 parent @MyStructInit : $@convention(method) (Int64, Int64, @thin MyStruct.Type) -> MyStruct }
|
||||
|
||||
// MyStruct.init(x:y:)
|
||||
sil hidden @MyStructInit : $@convention(method) (Int64, Int64, @thin MyStruct.Type) -> MyStruct {
|
||||
bb0(%0 : $Int64, %1 : $Int64, %2 : $@thin MyStruct.Type):
|
||||
%3 = struct $MyStruct (%0 : $Int64, %1 : $Int64), loc "sroa.swift":2:8, scope 1
|
||||
return %3 : $MyStruct, loc "sroa.swift":2:8, scope 1
|
||||
} // end sil function 'MyStructInit'
|
||||
|
||||
sil_scope 2 { loc "sroa.swift":7:6 parent @foo : $@convention(thin) (Int64, Int64) -> Int64 }
|
||||
|
||||
// CHECK-SROA-LABEL: sil {{.+}} @foo
|
||||
// foo(in_x:in_y:)
|
||||
sil hidden @foo : $@convention(thin) (Int64, Int64) -> Int64 {
|
||||
bb0(%0 : $Int64, %1 : $Int64):
|
||||
%4 = alloc_stack $MyStruct, var, name "my_struct", loc "sroa.swift":8:9, scope 2
|
||||
debug_value %4 : $*MyStruct, let, name "my_copy", expr op_deref, loc "sroa.swift":7:10, scope 2
|
||||
// Make sure SROA propagate the debug info to the splitted alloc_stack/debug_value instructions
|
||||
// CHECK-SROA: %[[ALLOC_X:[0-9]+]] = alloc_stack $Int64, var
|
||||
// CHECK-SROA-SAME: (name "my_struct", loc "sroa.swift":8:9
|
||||
// CHECK-SROA-SAME: type $*MyStruct, expr op_fragment:#MyStruct.x
|
||||
// CHECK-SROA-SAME: loc * "<compiler-generated>":0:0
|
||||
// CHECK-SROA: %[[ALLOC_Y:[0-9]+]] = alloc_stack $Int64, var
|
||||
// CHECK-SROA-SAME: (name "my_struct", loc "sroa.swift":8:9
|
||||
// CHECK-SROA-SAME: type $*MyStruct, expr op_fragment:#MyStruct.y
|
||||
// CHECK-SROA-SAME: loc * "<compiler-generated>":0:0
|
||||
// CHECK-SROA: debug_value %[[ALLOC_X]] : $*Int64, let
|
||||
// CHECK-SROA-SAME: name "my_copy",
|
||||
// CHECK-SROA-SAME: type $MyStruct, expr op_deref:op_fragment:#MyStruct.x
|
||||
// CHECK-SROA-SAME: loc "sroa.swift":7:10
|
||||
// CHECK-SROA: debug_value %[[ALLOC_Y]] : $*Int64, let
|
||||
// CHECK-SROA-SAME: name "my_copy",
|
||||
// CHECK-SROA-SAME: type $MyStruct, expr op_deref:op_fragment:#MyStruct.y
|
||||
// CHECK-SROA-SAME: loc "sroa.swift":7:10
|
||||
%13 = struct_element_addr %4 : $*MyStruct, #MyStruct.x, loc "sroa.swift":9:17, scope 2
|
||||
store %0 to %13 : $*Int64, loc "sroa.swift":9:17, scope 2
|
||||
// CHECK-SROA: store %0 to %[[ALLOC_X]]
|
||||
%15 = struct_element_addr %4 : $*MyStruct, #MyStruct.y, loc "sroa.swift":10:17, scope 2
|
||||
store %1 to %15 : $*Int64, loc "sroa.swift":10:17, scope 2
|
||||
// CHECK-SROA: store %1 to %[[ALLOC_Y]]
|
||||
dealloc_stack %4 : $*MyStruct, loc "sroa.swift":8:9, scope 2
|
||||
return %0 : $Int64, loc "sroa.swift":11:5, scope 2
|
||||
} // end sil function 'foo'
|
||||
28
test/DebugInfo/sroa_debug_value_empty.swift
Normal file
28
test/DebugInfo/sroa_debug_value_empty.swift
Normal file
@@ -0,0 +1,28 @@
|
||||
// RUN: %target-sil-opt -enable-sil-verify-all -sil-print-debuginfo -sroa %s | %FileCheck --check-prefix=CHECK-SROA %s
|
||||
sil_stage canonical
|
||||
|
||||
import Builtin
|
||||
import Swift
|
||||
|
||||
struct Empty {}
|
||||
|
||||
sil_scope 1 { loc "sroa.swift":7:6 parent @bar : $@convention(thin) (Int64, Int64) -> Int64 }
|
||||
|
||||
// CHECK-SROA-LABEL: sil {{.+}} @bar
|
||||
// bar(in_x:in_y:)
|
||||
sil hidden @bar : $@convention(thin) (Int64, Int64) -> Int64 {
|
||||
bb0(%0 : $Int64, %1 : $Int64):
|
||||
%4 = alloc_stack $Empty, var, name "my_struct", loc "sroa.swift":8:9, scope 1
|
||||
debug_value %4 : $*Empty, let, name "my_copy", expr op_deref, loc "sroa.swift":7:10, scope 1
|
||||
// Make sure SROA keeps the debug info
|
||||
// CHECK-SROA: debug_value undef : $*Empty, var
|
||||
// CHECK-SROA-SAME: name "my_struct",
|
||||
// CHECK-SROA-SAME: loc "sroa.swift":8:9
|
||||
// CHECK-SROA: debug_value undef : $*Empty, let
|
||||
// CHECK-SROA-SAME: name "my_copy",
|
||||
// CHECK-SROA-SAME: loc "sroa.swift":7:10
|
||||
%6 = struct $Empty (), loc "sroa.swift":9:8, scope 1
|
||||
store %6 to %4 : $*Empty, loc "sroa.swift":10:8, scope 1
|
||||
dealloc_stack %4 : $*Empty, loc "sroa.swift":8:9, scope 1
|
||||
return %0 : $Int64, loc "sroa.swift":11:5, scope 1
|
||||
} // end sil function 'foo'
|
||||
@@ -43,8 +43,6 @@ sil @use_int32 : $@convention(thin) (Builtin.Int32) -> ()
|
||||
sil @struct_with_scalar_fields : $@convention(thin) (S1) -> () {
|
||||
bb0(%0 : $S1):
|
||||
%1 = alloc_stack $S1
|
||||
debug_value %1 : $*S1 // should not prevent the optimization
|
||||
debug_value %1 : $*S1 // should not prevent the optimization
|
||||
store %0 to %1 : $*S1
|
||||
%2 = function_ref @use_int32 : $@convention(thin) (Builtin.Int32) -> ()
|
||||
%3 = struct_element_addr %1 : $*S1, #S1.y
|
||||
|
||||
@@ -46,8 +46,6 @@ sil [ossa] @use_int32 : $@convention(thin) (Builtin.Int32) -> ()
|
||||
sil [ossa] @struct_with_scalar_fields : $@convention(thin) (S1) -> () {
|
||||
bb0(%0 : $S1):
|
||||
%1 = alloc_stack [lexical] $S1
|
||||
debug_value %1 : $*S1 // should not prevent the optimization
|
||||
debug_value %1 : $*S1 // should not prevent the optimization
|
||||
store %0 to [trivial] %1 : $*S1
|
||||
%2 = function_ref @use_int32 : $@convention(thin) (Builtin.Int32) -> ()
|
||||
%3 = struct_element_addr %1 : $*S1, #S1.y
|
||||
|
||||
@@ -46,8 +46,6 @@ sil [ossa] @use_int32 : $@convention(thin) (Builtin.Int32) -> ()
|
||||
sil [ossa] @struct_with_scalar_fields : $@convention(thin) (S1) -> () {
|
||||
bb0(%0 : $S1):
|
||||
%1 = alloc_stack $S1
|
||||
debug_value %1 : $*S1 // should not prevent the optimization
|
||||
debug_value %1 : $*S1 // should not prevent the optimization
|
||||
store %0 to [trivial] %1 : $*S1
|
||||
%2 = function_ref @use_int32 : $@convention(thin) (Builtin.Int32) -> ()
|
||||
%3 = struct_element_addr %1 : $*S1, #S1.y
|
||||
@@ -483,7 +481,6 @@ sil [ossa] @use_obj : $@convention(thin) (@owned Obj) -> ()
|
||||
sil [ossa] @struct_with_obj_fields : $@convention(thin) (@owned NotTrivial) -> () {
|
||||
bb0(%0 : @owned $NotTrivial):
|
||||
%1 = alloc_stack $NotTrivial
|
||||
debug_value %1 : $*NotTrivial // should not prevent the optimization
|
||||
store %0 to [init] %1 : $*NotTrivial
|
||||
%2 = function_ref @use_obj : $@convention(thin) (@owned Obj) -> ()
|
||||
%3 = struct_element_addr %1 : $*NotTrivial, #NotTrivial.y
|
||||
|
||||
Reference in New Issue
Block a user