@@ -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.
4044CanonicalizeInstruction::~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;
0 commit comments