Skip to content

Commit ad7abdc

Browse files
authored
Merge pull request #40278 from atrick/fix-debuginfo
2 parents f16be55 + 7fec04c commit ad7abdc

File tree

13 files changed

+339
-97
lines changed

13 files changed

+339
-97
lines changed

include/swift/SILOptimizer/Utils/CanonicalizeInstruction.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,16 @@ struct CanonicalizeInstruction {
4242
const char *debugType = defaultDebugType;
4343
DeadEndBlocks &deadEndBlocks;
4444
InstModCallbacks callbacks;
45+
bool preserveDebugInfo;
4546

4647
CanonicalizeInstruction(const char *passDebugType,
4748
DeadEndBlocks &deadEndBlocks)
4849
: deadEndBlocks(deadEndBlocks),
4950
callbacks() {
51+
52+
preserveDebugInfo = getFunction()->getEffectiveOptimizationMode()
53+
<= OptimizationMode::NoOptimization;
54+
5055
#ifndef NDEBUG
5156
if (llvm::DebugFlag && !llvm::isCurrentDebugType(debugType))
5257
debugType = passDebugType;
@@ -68,6 +73,9 @@ struct CanonicalizeInstruction {
6873

6974
const SILFunction *getFunction() const { return deadEndBlocks.getFunction(); }
7075

76+
// TODO: callbacks should come from the current InstructionDeleter.
77+
InstModCallbacks &getCallbacks() { return callbacks; }
78+
7179
/// Rewrite this instruction, based on its operands and uses, into a more
7280
/// canonical representation.
7381
///

include/swift/SILOptimizer/Utils/DebugOptUtils.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#define SWIFT_SILOPTIMIZER_DEBUGOPTUTILS_H
2020

2121
#include "swift/SIL/DebugUtils.h"
22+
#include "swift/SIL/Projection.h"
2223
#include "swift/SIL/SILValue.h"
2324
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
2425

@@ -47,6 +48,13 @@ inline void deleteAllDebugUses(SILInstruction *inst,
4748
/// new `debug_value` instruction before \p I is deleted.
4849
void salvageDebugInfo(SILInstruction *I);
4950

51+
/// Create debug_value fragment for a new partial value.
52+
///
53+
/// Precondition: \p oldValue is a struct or class aggregate. \p proj projects a
54+
/// field from the aggregate into \p newValue corresponding to struct_extract.
55+
void createDebugFragments(SILValue oldValue, Projection proj,
56+
SILValue newValue);
57+
5058
/// Erases the instruction \p I from it's parent block and deletes it, including
5159
/// all debug instructions which use \p I.
5260
/// Precondition: The instruction may only have debug instructions as uses.

include/swift/SILOptimizer/Utils/OwnershipOptUtils.h

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -345,23 +345,17 @@ struct LoadOperation {
345345

346346
explicit operator bool() const { return !value.isNull(); }
347347

348-
SingleValueInstruction *operator*() const {
348+
SingleValueInstruction *getLoadInst() const {
349349
if (value.is<LoadInst *>())
350350
return value.get<LoadInst *>();
351351
return value.get<LoadBorrowInst *>();
352352
}
353353

354-
const SingleValueInstruction *operator->() const {
355-
if (value.is<LoadInst *>())
356-
return value.get<LoadInst *>();
357-
return value.get<LoadBorrowInst *>();
358-
}
354+
SingleValueInstruction *operator*() const { return getLoadInst(); }
359355

360-
SingleValueInstruction *operator->() {
361-
if (value.is<LoadInst *>())
362-
return value.get<LoadInst *>();
363-
return value.get<LoadBorrowInst *>();
364-
}
356+
const SingleValueInstruction *operator->() const { return getLoadInst(); }
357+
358+
SingleValueInstruction *operator->() { return getLoadInst(); }
365359

366360
SILValue getOperand() const {
367361
if (value.is<LoadInst *>())

lib/SILOptimizer/Transforms/DeadCodeElimination.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -581,9 +581,11 @@ bool DCE::removeDead() {
581581
// dead and will be deleted.
582582
// We do not have to end lifetime of a @guaranteed non phi arg.
583583
if (arg->getOwnershipKind() == OwnershipKind::Owned) {
584+
auto loc = RegularLocation::getAutoGeneratedLocation();
585+
// insertPt is non-null because Undef is non-owned.
584586
auto insertPt = getInsertAfterPoint(arg).getValue();
585587
SILBuilderWithScope builder(insertPt);
586-
auto *destroy = builder.createDestroyValue(insertPt->getLoc(), arg);
588+
auto *destroy = builder.createDestroyValue(loc, arg);
587589
LiveInstructions.insert(destroy);
588590
}
589591
i++;

lib/SILOptimizer/Utils/CanonicalizeInstruction.cpp

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ using namespace swift;
3636
// Tracing within the implementation can also be activiated by the pass.
3737
#define DEBUG_TYPE pass.debugType
3838

39+
llvm::cl::opt<bool> EnableLoadSplittingDebugInfo(
40+
"sil-load-splitting-debug-info", llvm::cl::init(false),
41+
llvm::cl::desc("Create debug fragments at -O for partial loads"));
42+
3943
// Vtable anchor.
4044
CanonicalizeInstruction::~CanonicalizeInstruction() {}
4145

@@ -243,6 +247,9 @@ splitAggregateLoad(LoadOperation loadInst, CanonicalizeInstruction &pass) {
243247
// }
244248
// }
245249
//
250+
// Also, avoid degrading debug info unless it is necessary for exclusivity
251+
// diagnostics.
252+
//
246253
// TODO: This logic subtly anticipates SILGen behavior. In the future, change
247254
// SILGen to avoid emitting the full load and never delete loads in Raw SIL.
248255
if (projections.empty() && loadInst->getModule().getStage() == SILStage::Raw)
@@ -294,14 +301,27 @@ splitAggregateLoad(LoadOperation loadInst, CanonicalizeInstruction &pass) {
294301
}
295302
pass.notifyNewInstruction(**lastNewLoad);
296303

304+
// FIXME: This drops debug info at -Onone load-splitting is required at
305+
// -Onone for exclusivity diagnostics. Fix this by
306+
//
307+
// 1. At -Onone, preserve the original load when pass.preserveDebugInfo is
308+
// true, but moving it out of its current access scope and into an "unknown"
309+
// access scope, which won't be enforced as an exclusivity violation.
310+
//
311+
// 2. At -O, create "debug fragments" recover as much debug info as possible
312+
// by creating debug_value fragments for each new partial load. Currently
313+
// disabled because of LLVM back-end crashes.
314+
if (!pass.preserveDebugInfo && EnableLoadSplittingDebugInfo) {
315+
createDebugFragments(*loadInst, proj, lastNewLoad->getLoadInst());
316+
}
297317
if (loadOwnership) {
298318
if (*loadOwnership == LoadOwnershipQualifier::Copy) {
299319
// Destroy the loaded value wherever the aggregate load was destroyed.
300320
assert(loadInst.getOwnershipQualifier() ==
301321
LoadOwnershipQualifier::Copy);
302322
for (SILInstruction *destroy : lifetimeEndingInsts) {
303323
auto *newInst = SILBuilderWithScope(destroy).createDestroyValue(
304-
destroy->getLoc(), **lastNewLoad);
324+
destroy->getLoc(), lastNewLoad->getLoadInst());
305325
pass.notifyNewInstruction(newInst);
306326
}
307327
}
@@ -324,6 +344,14 @@ splitAggregateLoad(LoadOperation loadInst, CanonicalizeInstruction &pass) {
324344
for (auto *destroy : lifetimeEndingInsts)
325345
nextII = killInstruction(destroy, nextII, pass);
326346

347+
// FIXME: remove this temporary hack to advance the iterator beyond
348+
// debug_value. A soon-to-be merged commit migrates CanonicalizeInstruction to
349+
// use InstructionDeleter.
350+
while (nextII != loadInst->getParent()->end()
351+
&& nextII->isDebugInstruction()) {
352+
++nextII;
353+
}
354+
deleteAllDebugUses(*loadInst, pass.getCallbacks());
327355
return killInstAndIncidentalUses(*loadInst, nextII, pass);
328356
}
329357

@@ -414,6 +442,14 @@ broadenSingleElementStores(StoreInst *storeInst,
414442
/// copy. The only way to guarantee the lifetime of a variable is to use a
415443
/// borrow scope--copy/destroy is insufficient by itself.
416444
///
445+
/// FIXME: This removes debug_value instructions aggressively as part of
446+
/// SILGenCleanup. Instead, debug_values should be canonicalized before copy
447+
/// elimination so that we never see the pattern:
448+
/// %b = begin_borrow
449+
/// %c = copy %b
450+
/// end_borrow %b
451+
/// debug_value %c
452+
///
417453
/// FIXME: Technically this should be guarded by a compiler flag like
418454
/// -enable-copy-propagation until SILGen protects scoped variables by
419455
/// borrow scopes.
@@ -423,20 +459,21 @@ eliminateSimpleCopies(CopyValueInst *cvi, CanonicalizeInstruction &pass) {
423459

424460
// Eliminate copies that only have destroy_value uses.
425461
SmallVector<DestroyValueInst *, 8> destroys;
426-
for (auto *use : getNonDebugUses(cvi)) {
462+
for (Operand *use : cvi->getUses()) {
427463
if (auto *dvi = dyn_cast<DestroyValueInst>(use->getUser())) {
428464
destroys.push_back(dvi);
429465
continue;
430466
}
467+
if (!pass.preserveDebugInfo && isa<DebugValueInst>(use->getUser())) {
468+
continue;
469+
}
431470
return next;
432471
}
433472

434473
while (!destroys.empty()) {
435474
next = killInstruction(destroys.pop_back_val(), next, pass);
436475
}
437-
438-
next = killInstAndIncidentalUses(cvi, next, pass);
439-
return next;
476+
return killInstAndIncidentalUses(cvi, next, pass);
440477
}
441478

442479
/// Unlike dead copy elimination, dead borrows can be safely removed because the
@@ -524,7 +561,6 @@ CanonicalizeInstruction::canonicalize(SILInstruction *inst) {
524561
if (auto li = LoadOperation(inst)) {
525562
return splitAggregateLoad(li, *this);
526563
}
527-
528564
if (auto *storeInst = dyn_cast<StoreInst>(inst)) {
529565
return broadenSingleElementStores(storeInst, *this);
530566
}
@@ -538,8 +574,11 @@ CanonicalizeInstruction::canonicalize(SILInstruction *inst) {
538574
// If we have ownership and are not in raw SIL, eliminate unneeded forwarding
539575
// insts. We don't do this in raw SIL as not to disturb the codegen read by
540576
// diagnostics.
577+
//
578+
// TODO: fix tryEliminateUnneededForwardingInst to handle debug uses.
541579
auto *fn = inst->getFunction();
542-
if (fn->hasOwnership() && fn->getModule().getStage() != SILStage::Raw) {
580+
if (!preserveDebugInfo && fn->hasOwnership()
581+
&& fn->getModule().getStage() != SILStage::Raw) {
543582
if (OwnershipForwardingMixin::isa(inst))
544583
if (auto newNext = tryEliminateUnneededForwardingInst(inst, *this))
545584
return *newNext;

lib/SILOptimizer/Utils/InstOptUtils.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1792,6 +1792,7 @@ void swift::endLifetimeAtLeakingBlocks(SILValue value,
17921792
});
17931793
}
17941794

1795+
// TODO: this currently fails to notify the pass with notifyNewInstruction.
17951796
void swift::salvageDebugInfo(SILInstruction *I) {
17961797
if (!I)
17971798
return;
@@ -1872,6 +1873,38 @@ void swift::salvageDebugInfo(SILInstruction *I) {
18721873
}
18731874
}
18741875

1876+
// TODO: this currently fails to notify the pass with notifyNewInstruction.
1877+
void swift::createDebugFragments(SILValue oldValue, Projection proj,
1878+
SILValue newValue) {
1879+
if (proj.getKind() != ProjectionKind::Struct)
1880+
return;
1881+
1882+
for (auto *use : getDebugUses(oldValue)) {
1883+
auto debugVal = dyn_cast<DebugValueInst>(use->getUser());
1884+
if (!debugVal)
1885+
continue;
1886+
1887+
// Can't create a fragment of a fragment.
1888+
auto varInfo = debugVal->getVarInfo();
1889+
if (!varInfo || varInfo->DIExpr.hasFragment())
1890+
continue;
1891+
1892+
SILType baseType = oldValue->getType();
1893+
1894+
// Copy VarInfo and add the corresponding fragment DIExpression.
1895+
SILDebugVariable newVarInfo = *varInfo;
1896+
newVarInfo.DIExpr.append(
1897+
SILDebugInfoExpression::createFragment(proj.getVarDecl(baseType)));
1898+
1899+
if (!newVarInfo.Type)
1900+
newVarInfo.Type = baseType;
1901+
1902+
// Create a new debug_value
1903+
SILBuilder(debugVal, debugVal->getDebugScope())
1904+
.createDebugValue(debugVal->getLoc(), newValue, newVarInfo);
1905+
}
1906+
}
1907+
18751908
IntegerLiteralInst *swift::optimizeBuiltinCanBeObjCClass(BuiltinInst *bi,
18761909
SILBuilder &builder) {
18771910
assert(bi->getBuiltinInfo().ID == BuiltinValueKind::CanBeObjCClass);

lib/SILOptimizer/Utils/InstructionDeleter.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@
1919
using namespace swift;
2020

2121
static bool hasOnlyIncidentalUses(SILInstruction *inst,
22-
bool disallowDebugUses = false) {
22+
bool preserveDebugInfo = false) {
2323
for (SILValue result : inst->getResults()) {
2424
for (Operand *use : result->getUses()) {
2525
SILInstruction *user = use->getUser();
2626
if (!isIncidentalUse(user))
2727
return false;
28-
if (disallowDebugUses && user->isDebugInstruction())
28+
if (preserveDebugInfo && user->isDebugInstruction())
2929
return false;
3030
}
3131
}
@@ -161,9 +161,9 @@ bool InstructionDeleter::trackIfDead(SILInstruction *inst) {
161161
}
162162

163163
void InstructionDeleter::forceTrackAsDead(SILInstruction *inst) {
164-
bool disallowDebugUses = inst->getFunction()->getEffectiveOptimizationMode()
164+
bool preserveDebugInfo = inst->getFunction()->getEffectiveOptimizationMode()
165165
<= OptimizationMode::NoOptimization;
166-
assert(hasOnlyIncidentalUses(inst, disallowDebugUses));
166+
assert(hasOnlyIncidentalUses(inst, preserveDebugInfo));
167167
getCallbacks().notifyWillBeDeleted(inst);
168168
deadInstructions.insert(inst);
169169
}
@@ -274,17 +274,16 @@ bool InstructionDeleter::deleteIfDead(SILInstruction *inst) {
274274

275275
void InstructionDeleter::forceDeleteAndFixLifetimes(SILInstruction *inst) {
276276
SILFunction *fun = inst->getFunction();
277-
bool disallowDebugUses =
277+
bool preserveDebugInfo =
278278
fun->getEffectiveOptimizationMode() <= OptimizationMode::NoOptimization;
279-
assert(hasOnlyIncidentalUses(inst, disallowDebugUses));
279+
assert(hasOnlyIncidentalUses(inst, preserveDebugInfo));
280280
deleteWithUses(inst, /*fixLifetimes*/ fun->hasOwnership());
281281
}
282282

283283
void InstructionDeleter::forceDelete(SILInstruction *inst) {
284-
bool disallowDebugUses =
285-
inst->getFunction()->getEffectiveOptimizationMode() <=
286-
OptimizationMode::NoOptimization;
287-
assert(hasOnlyIncidentalUses(inst, disallowDebugUses));
284+
bool preserveDebugInfo = inst->getFunction()->getEffectiveOptimizationMode()
285+
<= OptimizationMode::NoOptimization;
286+
assert(hasOnlyIncidentalUses(inst, preserveDebugInfo));
288287
deleteWithUses(inst, /*fixLifetimes*/ false);
289288
}
290289

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %target-swift-frontend %s -emit-sil -g -Osize -parse-stdlib -parse-as-library -enable-ossa-modules -o - | %FileCheck %s
2+
3+
// REQUIRES: asserts
4+
5+
import Swift
6+
7+
// Test that DCE correctly preserves debug locations.
8+
// SR-15300: Compiler crash when using Builtin.unreachable in initializers
9+
//
10+
// CHECK: sil_scope [[S1:[0-9]+]] { {{.*}} parent @$s18optimizer_pipeline1AVyACs13KeyValuePairsVyypypGcfC
11+
// CHECK: sil_scope [[S2:[0-9]+]] { {{.*}} parent [[S1]] }
12+
//
13+
// CHECK-LABEL: sil @$s18optimizer_pipeline1AVyACs13KeyValuePairsVyypypGcfC : $@convention(method) (@owned KeyValuePairs<Any, Any>, @thin A.Type) -> A {
14+
// CHECK: bb0(%0 : $KeyValuePairs<Any, Any>, %1 : $@thin A.Type):
15+
// CHECK: unreachable , scope [[S2]]
16+
// CHECK-LABEL: } // end sil function '$s18optimizer_pipeline1AVyACs13KeyValuePairsVyypypGcfC'
17+
public struct A {
18+
public init(_ pairs: KeyValuePairs<Any, Any>) {
19+
Builtin.unreachable()
20+
}
21+
}

test/SIL/crash-sr-15300.swift

Lines changed: 0 additions & 14 deletions
This file was deleted.

test/SILOptimizer/constant_evaluable_subset_test.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// Run the (mandatory) passes on which constant evaluator depends, and test the
55
// constant evaluator on the SIL produced after the dependent passes are run.
66
//
7-
// RUN: not %target-sil-opt -silgen-cleanup -raw-sil-inst-lowering -allocbox-to-stack -mandatory-inlining -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_silgen.sil > %t/constant_evaluable_subset_test.sil 2> %t/error-output
7+
// RUN: not %target-sil-opt -opt-mode=speed -silgen-cleanup -raw-sil-inst-lowering -allocbox-to-stack -mandatory-inlining -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_silgen.sil > %t/constant_evaluable_subset_test.sil 2> %t/error-output
88
//
99
// RUN: %FileCheck %S/Inputs/constant_evaluable.swift < %t/error-output
1010
//
@@ -14,6 +14,6 @@
1414
// especially performance inlining as it inlines functions such as String.+=
1515
// that the evaluator has special knowledge about.
1616
//
17-
// RUN: not %target-sil-opt -silgen-cleanup -diagnose-invalid-escaping-captures -diagnose-static-exclusivity -capture-promotion -access-enforcement-selection -allocbox-to-stack -noreturn-folding -definite-init -raw-sil-inst-lowering -closure-lifetime-fixup -semantic-arc-opts -destroy-hoisting -ownership-model-eliminator -mandatory-inlining -predictable-memaccess-opts -os-log-optimization -diagnostic-constant-propagation -predictable-deadalloc-elim -mandatory-arc-opts -diagnose-unreachable -diagnose-infinite-recursion -yield-once-check -dataflow-diagnostics -split-non-cond_br-critical-edges -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_silgen.sil > /dev/null 2> %t/error-output-mandatory
17+
// RUN: not %target-sil-opt -opt-mode=speed -silgen-cleanup -diagnose-invalid-escaping-captures -diagnose-static-exclusivity -capture-promotion -access-enforcement-selection -allocbox-to-stack -noreturn-folding -definite-init -raw-sil-inst-lowering -closure-lifetime-fixup -semantic-arc-opts -destroy-hoisting -ownership-model-eliminator -mandatory-inlining -predictable-memaccess-opts -os-log-optimization -diagnostic-constant-propagation -predictable-deadalloc-elim -mandatory-arc-opts -diagnose-unreachable -diagnose-infinite-recursion -yield-once-check -dataflow-diagnostics -split-non-cond_br-critical-edges -constexpr-limit 3000 -test-constant-evaluable-subset %t/constant_evaluable_subset_test_silgen.sil > /dev/null 2> %t/error-output-mandatory
1818
//
1919
// RUN: %FileCheck %S/Inputs/constant_evaluable.swift < %t/error-output-mandatory

0 commit comments

Comments
 (0)