Skip to content

Commit 875d111

Browse files
committed
CanonicalizeInstruction - FIXME and option to preserve debug info
Debug information needs to be preserved at -Onone. And it should be preserved to a reasonable extend at -O, not completely thrown away. This transformation does neither. The -Onone case is more important. The FIXME explains how that could be done. I'm including an example of how to handle the -O case, currently under a a flag, -sil-load-splitting-debug-info. This -O support uses debug fragments, which currently crashes when building SwiftPM's PackageModel module in LLVM's 'X86 Assembly Printer', llvm::DwarfExpression::addFragmentOffset. We don't appear to have any verification of debug fragment in either SIL or LLVM that would catch this. And I haven't been able to reduce the problem to a small test case.
1 parent 0eb7f46 commit 875d111

File tree

2 files changed

+69
-2
lines changed

2 files changed

+69
-2
lines changed

lib/SILOptimizer/Utils/CanonicalizeInstruction.cpp

Lines changed: 29 additions & 2 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

@@ -533,7 +561,6 @@ CanonicalizeInstruction::canonicalize(SILInstruction *inst) {
533561
if (auto li = LoadOperation(inst)) {
534562
return splitAggregateLoad(li, *this);
535563
}
536-
537564
if (auto *storeInst = dyn_cast<StoreInst>(inst)) {
538565
return broadenSingleElementStores(storeInst, *this);
539566
}

test/SILOptimizer/silgen_cleanup.sil

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ struct UInt8 {
2626
var _value : Builtin.Int8
2727
}
2828

29+
// =============================================================================
30+
// Test splitAggregateLoad
31+
//
32+
// Required for exclusivity diagnostics that are consistent across -Onone / -O
33+
// =============================================================================
34+
2935
// CHECK-LABEL: sil [ossa] @struct_extract_load_to_load_struct_element_addr
3036
// CHECK: bb0([[IN:%[0-9]+]] : $*UInt8):
3137
// CHECK-NEXT: [[IN_GEP:%[0-9]+]] = struct_element_addr [[IN]] : $*UInt8, #UInt8._value
@@ -187,6 +193,36 @@ bb0(%0 : $*X1):
187193
return %result : $(Int, Builtin.NativeObject, Builtin.NativeObject)
188194
}
189195

196+
sil private [ossa] @testLoadWithDebugInfo : $@convention(thin) (@inout_aliasable X1) -> (Int, @owned Builtin.NativeObject, @owned Builtin.NativeObject) {
197+
bb0(%0 : $*X1):
198+
%access = begin_access [read] [unknown] %0 : $*X1
199+
%load = load [copy] %access : $*X1
200+
debug_value %load : $Int, let, name "x1"
201+
end_access %access : $*X1
202+
203+
%borrowa = begin_borrow %load : $X1
204+
%a = struct_extract %borrowa : $X1, #X1.a
205+
end_borrow %borrowa : $X1
206+
207+
%borrow1 = begin_borrow %load : $X1
208+
%o1 = struct_extract %borrow1 : $X1, #X1.obj1
209+
%copy1 = copy_value %o1 : $Builtin.NativeObject
210+
end_borrow %borrow1 : $X1
211+
212+
%borrow2 = begin_borrow %load : $X1
213+
%o2 = struct_extract %borrow2 : $X1, #X1.obj2
214+
%copy2 = copy_value %o2 : $Builtin.NativeObject
215+
end_borrow %borrow2 : $X1
216+
217+
destroy_value %load : $X1
218+
219+
%result = tuple (%a : $Int, %copy1 : $Builtin.NativeObject, %copy2 : $Builtin.NativeObject)
220+
return %result : $(Int, Builtin.NativeObject, Builtin.NativeObject)
221+
}
222+
223+
// =============================================================================
224+
// Test broadenSingleElementStores
225+
// =============================================================================
190226

191227
struct X2 {
192228
@_hasStorage @_hasInitialValue var obj: Builtin.NativeObject { get set }
@@ -217,6 +253,10 @@ bb0(%0 : $*X3, %1 : @guaranteed $Builtin.NativeObject):
217253
return %12 : $()
218254
}
219255

256+
// =============================================================================
257+
// Test simple copy and borrow elimination
258+
// =============================================================================
259+
220260
// We used to hit a memory error on this test.
221261
//
222262
// CHECK-LABEL: sil [ossa] @testDestructureTupleNoCrash : $@convention(thin) (@owned (Builtin.NativeObject, Builtin.NativeObject)) -> () {

0 commit comments

Comments
 (0)