Skip to content

Commit b85e699

Browse files
committed
[DebugInfo] [Mem2Reg] Move debug info promotion to salvageDebugInfo
1 parent f4d7327 commit b85e699

File tree

10 files changed

+77
-125
lines changed

10 files changed

+77
-125
lines changed

include/swift/SIL/SILBuilder.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,11 @@ class SILBuilder {
262262
/// scopes. To avoid a verification error later in the pipeline, drop all
263263
/// variables without a proper source location.
264264
bool shouldDropVariable(SILDebugVariable Var, SILLocation Loc) {
265-
return !Var.ArgNo && Loc.isSynthesizedAST();
265+
if (Var.ArgNo)
266+
return false;
267+
if (Var.Loc)
268+
return Var.Loc->isSynthesizedAST();
269+
return Loc.isSynthesizedAST();
266270
}
267271

268272

include/swift/SILOptimizer/Utils/DebugOptUtils.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ inline void deleteAllDebugUses(SILInstruction *inst,
4848
/// new `debug_value` instruction before \p I is deleted.
4949
void salvageDebugInfo(SILInstruction *I);
5050

51+
/// Transfer debug info associated with the store-like instruction \p SI to a
52+
/// new `debug_value` instruction before \p SI is deleted.
53+
/// @param SI The store instruction being deleted
54+
/// @param SrcVal The old source, where the debuginfo should be propagated to
55+
/// @param DestVal The old destination, where the debuginfo was
56+
void salvageStoreDebugInfo(SILInstruction *SI,
57+
SILValue SrcVal, SILValue DestVal);
58+
5159
/// Transfer debug information associated with the result of \p load to the
5260
/// load's address operand.
5361
///

lib/SILOptimizer/Transforms/SILMem2Reg.cpp

Lines changed: 3 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -301,68 +301,6 @@ replaceDestroy(DestroyAddrInst *dai, SILValue newValue, SILBuilderContext &ctx,
301301
prepareForDeletion(dai, instructionsToDelete);
302302
}
303303

304-
/// Whether the specified debug_value's operand names the address at the
305-
/// indicated alloc_stack.
306-
///
307-
/// If it's a guaranteed alloc_stack (i.e. a store_borrow location), that
308-
/// includes the values produced by any store_borrows whose destinations are the
309-
/// alloc_stack since those values amount to aliases for the alloc_stack's
310-
/// storage.
311-
static bool isDebugValueOfAllocStack(DebugValueInst *dvi, AllocStackInst *asi) {
312-
auto value = dvi->getOperand();
313-
if (value == asi)
314-
return true;
315-
auto *sbi = dyn_cast<StoreBorrowInst>(value);
316-
if (!sbi)
317-
return false;
318-
return sbi->getDest() == asi;
319-
}
320-
321-
/// Promote a DebugValue w/ address value to a DebugValue of non-address value.
322-
static void promoteDebugValueAddr(DebugValueInst *dvai, SILValue value,
323-
SILBuilderContext &ctx,
324-
InstructionDeleter &deleter) {
325-
assert(dvai->getOperand()->getType().isLoadable(*dvai->getFunction()) &&
326-
"Unexpected promotion of address-only type!");
327-
assert(value && "Expected valid value");
328-
329-
// Avoid inserting the same debug_value twice.
330-
//
331-
// We remove the di expression when comparing since:
332-
//
333-
// 1. dvai is on will always have the deref diexpr since it is on addresses.
334-
//
335-
// 2. We are only trying to delete debug_var that are on values... values will
336-
// never have an op_deref meaning that the comparison will always fail and
337-
// not serve out purpose here.
338-
auto dvaiWithoutDIExpr = dvai->getVarInfo()->withoutDIExpr();
339-
for (auto *use : value->getUses()) {
340-
if (auto *dvi = dyn_cast<DebugValueInst>(use->getUser())) {
341-
if (!dvi->hasAddrVal() && *dvi->getVarInfo() == dvaiWithoutDIExpr) {
342-
deleter.forceDelete(dvai);
343-
return;
344-
}
345-
}
346-
}
347-
348-
// Drop op_deref if dvai is actually a debug_value instruction
349-
auto varInfo = *dvai->getVarInfo();
350-
if (isa<DebugValueInst>(dvai)) {
351-
auto &diExpr = varInfo.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())
358-
diExpr.eraseElement(diExpr.element_begin());
359-
}
360-
361-
SILBuilderWithScope b(dvai, ctx);
362-
b.createDebugValue(dvai->getLoc(), value, std::move(varInfo));
363-
deleter.forceDelete(dvai);
364-
}
365-
366304
/// Returns true if \p I is a load which loads from \p ASI.
367305
static bool isLoadFromStack(SILInstruction *i, AllocStackInst *asi) {
368306
if (!isa<LoadInst>(i) && !isa<LoadBorrowInst>(i))
@@ -1172,14 +1110,8 @@ SILInstruction *StackAllocationPromoter::promoteAllocationInBlock(
11721110
continue;
11731111
}
11741112

1175-
// Replace debug_value w/ address value with debug_value of
1176-
// the promoted value.
1177-
// if we have a valid value to use at this point. Otherwise we'll
1178-
// promote this when we deal with hooking up phis.
1113+
// Debug values will automatically be salvaged, we can ignore them
11791114
if (auto *dvi = DebugValueInst::hasAddrVal(inst)) {
1180-
if (isDebugValueOfAllocStack(dvi, asi) && runningVals)
1181-
promoteDebugValueAddr(dvi, runningVals->value.replacement(asi, dvi),
1182-
ctx, deleter);
11831115
continue;
11841116
}
11851117

@@ -1432,12 +1364,8 @@ void StackAllocationPromoter::fixBranchesAndUses(
14321364
// on.
14331365
SILBasicBlock *userBlock = user->getParent();
14341366

1367+
// Debug values will automatically be salvaged, we can ignore them
14351368
if (auto *dvi = DebugValueInst::hasAddrVal(user)) {
1436-
// Replace debug_value w/ address-type value with
1437-
// a new debug_value w/ promoted value.
1438-
auto def = getEffectiveLiveInValues(phiBlocks, userBlock);
1439-
promoteDebugValueAddr(dvi, def.replacement(asi, dvi), ctx, deleter);
1440-
++NumInstRemoved;
14411369
continue;
14421370
}
14431371

@@ -2036,20 +1964,8 @@ void MemoryToRegisters::removeSingleBlockAllocation(AllocStackInst *asi) {
20361964
continue;
20371965
}
20381966

2039-
// Replace debug_value w/ address value with debug_value of
2040-
// the promoted value.
1967+
// Debug values will automatically be salvaged, we can ignore them
20411968
if (auto *dvi = DebugValueInst::hasAddrVal(inst)) {
2042-
if (isDebugValueOfAllocStack(dvi, asi)) {
2043-
if (runningVals) {
2044-
promoteDebugValueAddr(dvi, runningVals->value.replacement(asi, dvi),
2045-
ctx, deleter);
2046-
} else {
2047-
// Drop debug_value of uninitialized void values.
2048-
assert(asi->getElementType().isVoid() &&
2049-
"Expected initialization of non-void type!");
2050-
deleter.forceDelete(dvi);
2051-
}
2052-
}
20531969
continue;
20541970
}
20551971

lib/SILOptimizer/Utils/GenericCloner.cpp

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ void GenericCloner::populateCloned() {
6868
SILBasicBlock *ClonedEntryBB = Cloned->createBasicBlock();
6969
getBuilder().setInsertionPoint(ClonedEntryBB);
7070

71+
RemappedScopeCache.insert({Original.getDebugScope(), Cloned->getDebugScope()});
72+
7173
// Create the entry basic block with the function arguments.
7274
auto origConv = Original.getConventions();
7375
unsigned ArgIdx = 0;
@@ -138,26 +140,6 @@ void GenericCloner::populateCloned() {
138140
mappedType, OrigArg->getDecl());
139141
NewArg->copyFlags(cast<SILFunctionArgument>(OrigArg));
140142

141-
// Try to create a new debug_value from an existing debug_value w/
142-
// address value for the argument. We do this before storing to
143-
// ensure that when we are cloning code in ossa the argument has
144-
// not been consumed by the store below.
145-
for (Operand *ArgUse : OrigArg->getUses()) {
146-
if (auto *DVI = DebugValueInst::hasAddrVal(ArgUse->getUser())) {
147-
auto *oldScope = getBuilder().getCurrentDebugScope();
148-
getBuilder().setCurrentDebugScope(
149-
remapScope(DVI->getDebugScope()));
150-
auto VarInfo = DVI->getVarInfo();
151-
assert(VarInfo && VarInfo->DIExpr &&
152-
"No DebugVarInfo or no DIExpr operand?");
153-
// Drop the op_deref
154-
VarInfo->DIExpr.eraseElement(VarInfo->DIExpr.element_begin());
155-
getBuilder().createDebugValue(DVI->getLoc(), NewArg, *VarInfo);
156-
getBuilder().setCurrentDebugScope(oldScope);
157-
break;
158-
}
159-
}
160-
161143
// Store the new direct parameter to an alloc_stack.
162144
createAllocStack();
163145
SILValue addr;

lib/SILOptimizer/Utils/InstOptUtils.cpp

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1826,6 +1826,36 @@ void swift::endLifetimeAtLeakingBlocks(SILValue value,
18261826
});
18271827
}
18281828

1829+
// Replace a store with an appropriate debug value.
1830+
static void transferStoreDebugValue(DebugVarCarryingInst DefiningInst,
1831+
SILInstruction *SI,
1832+
SILValue original) {
1833+
auto VarInfo = DefiningInst.getVarInfo();
1834+
if (!VarInfo)
1835+
return;
1836+
if (!VarInfo->Loc && !SI->getLoc().hasSameSourceLocation(DefiningInst->getLoc()))
1837+
VarInfo->Loc = DefiningInst->getLoc();
1838+
if (!VarInfo->Scope && SI->getDebugScope() != DefiningInst->getDebugScope())
1839+
VarInfo->Scope = DefiningInst->getDebugScope();
1840+
if (!isa<CopyAddrInst>(SI) && VarInfo->DIExpr.startsWithDeref())
1841+
VarInfo->DIExpr.eraseElement(VarInfo->DIExpr.element_begin());
1842+
// Note: The instruction should logically be in the SI's scope.
1843+
// However, LLVM does not support variables and stores in different scopes,
1844+
// so we use the variable's scope.
1845+
SILBuilder(SI, DefiningInst->getDebugScope())
1846+
.createDebugValue(SI->getLoc(), original, *VarInfo);
1847+
}
1848+
1849+
void swift::salvageStoreDebugInfo(SILInstruction *SI,
1850+
SILValue SrcVal, SILValue DestVal) {
1851+
if (auto *ASI = dyn_cast_or_null<AllocStackInst>(
1852+
DestVal.getDefiningInstruction())) {
1853+
transferStoreDebugValue(ASI, SI, SrcVal);
1854+
for (Operand *U : getDebugUses(ASI))
1855+
transferStoreDebugValue(U->getUser(), SI, SrcVal);
1856+
}
1857+
}
1858+
18291859
// TODO: this currently fails to notify the pass with notifyNewInstruction.
18301860
//
18311861
// TODO: whenever a debug_value is inserted at a new location, check that no
@@ -1837,18 +1867,13 @@ void swift::salvageDebugInfo(SILInstruction *I) {
18371867

18381868
if (auto *SI = dyn_cast<StoreInst>(I)) {
18391869
if (SILValue DestVal = SI->getDest())
1840-
if (auto *ASI = dyn_cast_or_null<AllocStackInst>(
1841-
DestVal.getDefiningInstruction())) {
1842-
if (auto VarInfo = ASI->getVarInfo()) {
1843-
// Always propagate destination location for incoming arguments (as
1844-
// their location must be unique) as well as when store source
1845-
// location is compiler-generated.
1846-
bool UseDestLoc = VarInfo->ArgNo || SI->getLoc().isAutoGenerated();
1847-
SILBuilder(SI, ASI->getDebugScope())
1848-
.createDebugValue(UseDestLoc ? ASI->getLoc() : SI->getLoc(),
1849-
SI->getSrc(), *VarInfo);
1850-
}
1851-
}
1870+
salvageStoreDebugInfo(SI, SI->getSrc(), DestVal);
1871+
}
1872+
if (auto *SI = dyn_cast<StoreBorrowInst>(I)) {
1873+
if (SILValue DestVal = SI->getDest())
1874+
salvageStoreDebugInfo(SI, SI->getSrc(), DestVal);
1875+
for (Operand *U : getDebugUses(SI))
1876+
transferStoreDebugValue(U->getUser(), SI, SI->getSrc());
18521877
}
18531878
// If a `struct` SIL instruction is "unwrapped" and removed,
18541879
// for instance, in favor of using its enclosed value directly,

lib/SILOptimizer/Utils/InstructionDeleter.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ void InstructionDeleter::deleteWithUses(SILInstruction *inst, bool fixLifetimes,
228228
// Recursively visit all uses while growing toDeleteInsts in def-use order and
229229
// dropping dead operands.
230230
SmallVector<SILInstruction *, 4> toDeleteInsts;
231+
SmallVector<Operand *, 4> toDropUses;
231232

232233
toDeleteInsts.push_back(inst);
233234
swift::salvageDebugInfo(inst);
@@ -242,11 +243,15 @@ void InstructionDeleter::deleteWithUses(SILInstruction *inst, bool fixLifetimes,
242243
assert(!isa<BranchInst>(user) && "can't delete phis");
243244

244245
toDeleteInsts.push_back(user);
246+
toDropUses.push_back(use);
245247
swift::salvageDebugInfo(user);
246-
use->drop();
247248
}
248249
}
249250
}
251+
// Drop all after salvage debug info has been run.
252+
for (auto *use : toDropUses) {
253+
use->drop();
254+
}
250255
// Process the remaining operands. Insert destroys for consuming
251256
// operands. Track newly dead operand values. Instructions with multiple dead
252257
// operands may occur in toDeleteInsts multiple times.

test/AutoDiff/compiler_crashers_fixed/58660-conflicting-debug-info-inlining.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ struct MyModel: Differentiable {
4747
// CHECK-LABEL: // pullback of MyModel.member4()
4848
// CHECK-NOT: debug_value %{{.*}} : $MyModel.TangentVector, var, name %{{.*}}, argno 1, scope
4949
// CHECK: bb0(%{{.*}} : $_AD__$s4main7MyModelV7member4yyF_bb3__Pred__src_0_wrt_0):
50-
// CHECK: debug_value %{{.*}} : $MyModel.TangentVector, var, name "derivative of 'self' in scope at {{.*}} (scope #1)", scope
50+
// CHECK: debug_value %{{.*}} : $MyModel.TangentVector, var, (name "derivative of 'self' in scope at {{.*}} (scope #1)"{{.*}}), scope
5151
// Must be a differentiable type.
5252
var localVar: Float = 0
5353

test/DebugInfo/sroa_debug_value.sil

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %target-sil-opt -enable-sil-verify-all -sil-print-debuginfo -sroa %s | %FileCheck --check-prefix=CHECK-SROA %s
2+
// RUN: %target-sil-opt -enable-sil-verify-all -sil-print-debuginfo -sroa -mem2reg %s | %FileCheck --check-prefix=CHECK-MEM2REG %s
23
sil_stage canonical
34

45
import Builtin
@@ -51,5 +52,14 @@ bb0(%0 : $Int64, %1 : $Int64):
5152
store %1 to %15 : $*Int64, loc "sroa.swift":10:17, scope 2
5253
// CHECK-SROA: store %1 to %[[ALLOC_Y]]
5354
dealloc_stack %4 : $*MyStruct, loc "sroa.swift":8:9, scope 2
55+
// Make sure function arguments' SSA values are properly connected to both source variables
56+
// CHECK-MEM2REG: debug_value %0 : $Int64, var, (name "my_struct", loc "sroa.swift":8:9
57+
// CHECK-MEM2REG-SAME: type $*MyStruct, expr op_fragment:#MyStruct.x
58+
// CHECK-MEM2REG: debug_value %0 : $Int64, let, (name "my_copy", loc "sroa.swift":7:10
59+
// CHECK-MEM2REG-SAME: type $MyStruct, expr op_fragment:#MyStruct.x
60+
// CHECK-MEM2REG: debug_value %1 : $Int64, var, (name "my_struct", loc "sroa.swift":8:9
61+
// CHECK-MEM2REG-SAME: type $*MyStruct, expr op_fragment:#MyStruct.y
62+
// CHECK-MEM2REG: debug_value %1 : $Int64, let, (name "my_copy", loc "sroa.swift":7:10
63+
// CHECK-MEM2REG-SAME: type $MyStruct, expr op_fragment:#MyStruct.y
5464
return %0 : $Int64, loc "sroa.swift":11:5, scope 2
5565
} // end sil function 'foo'

test/SILOptimizer/mem2reg_lifetime_nontrivial.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -595,8 +595,8 @@ bb3:
595595
// CHECK-NOT: alloc_stack
596596
// CHECK-NOT: debug_value {{.*}} expr op_deref
597597
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned $Klass):
598+
// CHECK: debug_value [[INSTANCE]]
598599
// CHECK: [[LIFETIME_OWNED:%[^,]+]] = move_value [lexical] [[INSTANCE]]
599-
// CHECK: debug_value [[LIFETIME_OWNED]]
600600
// CHECK-LABEL: } // end sil function 'mem2reg_debug_value'
601601
sil [ossa] @mem2reg_debug_value : $@convention(thin) (@owned Klass) -> @owned Klass {
602602
bb0(%0 : @owned $Klass):

test/SILOptimizer/mem2reg_resilient_lifetime.sil

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,19 @@ public struct ResilientStruct {
1111
// CHECK-LABEL: sil [ossa] @mem2reg_debug_value :
1212
// CHECK: {{bb[^,]+}}([[REGISTER_0:%[^,]+]] : $*ResilientStruct):
1313
// CHECK-NEXT: [[REGISTER_1:%[^,]+]] = load [copy] [[REGISTER_0]]
14+
// CHECK-NEXT: debug_value [[REGISTER_1]] : $ResilientStruct, var, name "before"
1415
// CHECK-NEXT: [[REGISTER_3:%[^,]+]] = move_value [lexical] [[REGISTER_1]]
15-
// CHECK-NEXT: debug_value [[REGISTER_3]]
16+
// CHECK-NEXT: debug_value [[REGISTER_3]] : $ResilientStruct, let, name "after"
1617
// CHECK-NEXT: destroy_value [[REGISTER_3]]
1718
// CHECK-LABEL: } // end sil function 'mem2reg_debug_value'
1819
sil [ossa] @mem2reg_debug_value : $@convention(thin) (@in_guaranteed ResilientStruct) -> () {
1920
bb0(%0 : $*ResilientStruct):
2021
%1 = alloc_stack [lexical] $ResilientStruct
2122
%2 = load [copy] %0 : $*ResilientStruct
2223
store %2 to [init] %1 : $*ResilientStruct
23-
debug_value %1 : $*ResilientStruct, expr op_deref
24+
debug_value %1 : $*ResilientStruct, var, name "before", expr op_deref
2425
%3 = load [take] %1 : $*ResilientStruct
26+
debug_value %3 : $ResilientStruct, let, name "after"
2527
destroy_value %3 : $ResilientStruct
2628
dealloc_stack %1 : $*ResilientStruct
2729
%4 = tuple ()

0 commit comments

Comments
 (0)