Skip to content

Commit c3787df

Browse files
committed
[move-only] Remove misguided handling of inout.
I think this was just an incorrect fix. The actual issue is that the memory lifetime verifier views debug_value as requiring a live address... but at the same time, we do not want to emit diagnostics for a debug_value... we want to do it for other things. So my solution is to add debug_value to the final liveness computation, but not use it earlier when we use said liveness to compute diagnostics. rdar://106442224
1 parent d1b206b commit c3787df

File tree

3 files changed

+109
-20
lines changed

3 files changed

+109
-20
lines changed

lib/SILOptimizer/Mandatory/MoveOnlyAddressCheckerUtils.cpp

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,11 @@ struct UseState {
410410
llvm::SmallMapVector<SILInstruction *, TypeTreeLeafTypeRange, 4> reinitInsts;
411411
SmallSetVector<SILInstruction *, 2> inoutTermUsers;
412412

413+
/// We add debug_values to liveness late after we diagnose, but before we
414+
/// hoist destroys to ensure that we do not hoist destroys out of access
415+
/// scopes.
416+
DebugValueInst *debugValue = nullptr;
417+
413418
SILFunction *getFunction() const { return address->getFunction(); }
414419

415420
/// Returns true if this is a terminator instruction that although it doesn't
@@ -429,6 +434,7 @@ struct UseState {
429434
initInsts.clear();
430435
reinitInsts.clear();
431436
inoutTermUsers.clear();
437+
debugValue = nullptr;
432438
}
433439

434440
void dump() {
@@ -465,6 +471,10 @@ struct UseState {
465471
for (auto *inst : inoutTermUsers) {
466472
llvm::dbgs() << *inst;
467473
}
474+
llvm::dbgs() << "Debug Value User:\n";
475+
if (debugValue) {
476+
llvm::dbgs() << *debugValue;
477+
}
468478
}
469479

470480
void
@@ -1198,10 +1208,6 @@ bool GatherUsesVisitor::visitUse(Operand *op, AccessUseType useTy) {
11981208
return true;
11991209
}
12001210

1201-
// We don't care about debug instructions.
1202-
if (op->getUser()->isDebugInstruction())
1203-
return true;
1204-
12051211
// For convenience, grab the user of op.
12061212
auto *user = op->getUser();
12071213

@@ -1261,6 +1267,19 @@ bool GatherUsesVisitor::visitUse(Operand *op, AccessUseType useTy) {
12611267
if (isa<EndAccessInst>(user))
12621268
return true;
12631269

1270+
if (auto *di = dyn_cast<DebugValueInst>(user)) {
1271+
// Make sure that our debug_value is always on our root value. If not, we
1272+
// have something we don't understand and should bail. This ensures we can
1273+
// always hoist the debug_value to our mark_must_check. This ensures that by
1274+
// marking debug_value later as requiring liveness, we do not change our
1275+
// liveness calculation since values are always live at the mark_must_check.
1276+
if (di->getOperand() != getRootAddress())
1277+
return false;
1278+
1279+
useState.debugValue = di;
1280+
return true;
1281+
}
1282+
12641283
// At this point, we have handled all of the non-loadTakeOrCopy/consuming
12651284
// uses.
12661285
if (auto *copyAddr = dyn_cast<CopyAddrInst>(user)) {
@@ -1943,21 +1962,6 @@ void MoveOnlyAddressCheckerPImpl::insertDestroysOnBoundary(
19431962
liveness.getRootValue(), defPair.second,
19441963
consumes);
19451964
} else {
1946-
// If our dead def is a mark_must_check and we are processing an inout
1947-
// argument, do not insert a destroy_addr. We are cheating a little bit by
1948-
// modeling the initial value as a mark_must_check... so we need to
1949-
// compensate for our cheating by not inserting the destroy_addr here
1950-
// since we would be destroying the inout argument before we use it.
1951-
if (auto *markMustCheckInst =
1952-
dyn_cast<MarkMustCheckInst>(defPair.first)) {
1953-
if (auto *arg = dyn_cast<SILFunctionArgument>(
1954-
markMustCheckInst->getOperand())) {
1955-
if (arg->getArgumentConvention().isInoutConvention()) {
1956-
continue;
1957-
}
1958-
}
1959-
}
1960-
19611965
auto *inst = cast<SILInstruction>(defPair.first);
19621966
auto *insertPt = inst->getNextInstruction();
19631967
assert(insertPt && "def instruction was a terminator");
@@ -2144,9 +2148,28 @@ bool MoveOnlyAddressCheckerPImpl::performSingleCheck(
21442148
return true;
21452149
}
21462150

2151+
//===
2152+
// Final Transformation
2153+
//
2154+
21472155
// Ok, we now know that we fit our model since we did not emit errors and thus
21482156
// can begin the transformation.
21492157
SWIFT_DEFER { consumes.clear(); };
2158+
2159+
// First add any debug_values that we saw as liveness uses. This is important
2160+
// since the debugger wants to see live values when we define a debug_value,
2161+
// but we do not want to use them earlier when emitting diagnostic errors.
2162+
if (auto *di = addressUseState.debugValue) {
2163+
// Move the debug_value to right before the markedAddress to ensure that we
2164+
// do not actually change our liveness computation.
2165+
//
2166+
// NOTE: The author is not sure if this can ever happen with SILGen output,
2167+
// but this is being put just to be safe.
2168+
di->moveAfter(markedAddress);
2169+
liveness.updateForUse(di, TypeTreeLeafTypeRange(markedAddress),
2170+
false /*lifetime ending*/);
2171+
}
2172+
21502173
FieldSensitivePrunedLivenessBoundary boundary(liveness.getNumSubElements());
21512174
liveness.computeBoundary(boundary);
21522175
insertDestroysOnBoundary(markedAddress, liveness, boundary);

test/SILOptimizer/moveonly_addresschecker.sil

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,14 @@ bb0(%0 : @owned $Klass, %1 : @owned $Klass):
339339
}
340340

341341
// CHECK-LABEL: sil [ossa] @myBufferViewSetter : $@convention(method) <T> (UnsafeBufferPointer<T>, @inout MyBufferView<T>) -> () {
342-
// CHECK-NOT: destroy_addr
342+
// CHECK: bb0([[ARG0:%.*]] : ${{.*}}, [[ARG1:%.*]] :
343+
// CHECK: debug_value [[ARG0]]
344+
// CHECK: debug_value [[ARG1]]
345+
// CHECK: destroy_addr [[ARG1]]
346+
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [static] [[ARG1]]
347+
// CHECK: [[GEP:%.*]] = struct_element_addr [[ACCESS]]
348+
// CHECK: store [[ARG0]] to [trivial] [[GEP]]
349+
// CHECK: end_access [[ACCESS]]
343350
// CHECK: } // end sil function 'myBufferViewSetter'
344351
sil [ossa] @myBufferViewSetter : $@convention(method) <T> (UnsafeBufferPointer<T>, @inout MyBufferView<T>) -> () {
345352
bb0(%0 : $UnsafeBufferPointer<T>, %1 : $*MyBufferView<T>):
@@ -408,3 +415,25 @@ bb0:
408415
%15 = tuple ()
409416
return %15 : $()
410417
}
418+
419+
// CHECK-LABEL: sil [ossa] @classSimpleChainArgTestHoistDebugValue : $@convention(thin) (@inout Klass) -> () {
420+
// CHECK: bb0([[ARG:%.*]] :
421+
// CHECK-NEXT: debug_value [[ARG]]
422+
sil [ossa] @classSimpleChainArgTestHoistDebugValue : $@convention(thin) (@inout Klass) -> () {
423+
bb0(%0 : $*Klass):
424+
%1 = mark_must_check [consumable_and_assignable] %0 : $*Klass
425+
%3 = alloc_stack [lexical] $Klass, var, name "y2"
426+
%4 = mark_must_check [consumable_and_assignable] %3 : $*Klass
427+
%5 = begin_access [read] [static] %1 : $*Klass
428+
copy_addr %5 to [init] %4 : $*Klass
429+
end_access %5 : $*Klass
430+
// If we do not hoist this to %1, we should crash. Make sure we dont!
431+
debug_value %1 : $*Klass, var, name "x2", argno 1, expr op_deref
432+
destroy_addr %4 : $*Klass
433+
dealloc_stack %3 : $*Klass
434+
%f = function_ref @get_klass : $@convention(thin) () -> @owned Klass
435+
%newArg = apply %f() : $@convention(thin) () -> @owned Klass
436+
store %newArg to [init] %1 : $*Klass
437+
%27 = tuple ()
438+
return %27 : $()
439+
}

test/SILOptimizer/moveonly_addresschecker_diagnostics.sil

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,3 +279,40 @@ bb0(%0 : @owned $NonTrivialStruct):
279279
%11 = tuple ()
280280
return %11 : $()
281281
}
282+
283+
284+
sil [ossa] @classSimpleChainArgTest : $@convention(thin) (@inout Klass) -> () {
285+
bb0(%0 : $*Klass):
286+
%1 = mark_must_check [consumable_and_assignable] %0 : $*Klass
287+
// expected-error @-1 {{'x2' consumed more than once}}
288+
// expected-error @-2 {{'x2' consumed but not reinitialized before end of function}}
289+
debug_value %1 : $*Klass, var, name "x2", argno 1, expr op_deref
290+
%3 = alloc_stack [lexical] $Klass, var, name "y2"
291+
%4 = mark_must_check [consumable_and_assignable] %3 : $*Klass
292+
%5 = begin_access [read] [static] %1 : $*Klass
293+
copy_addr %5 to [init] %4 : $*Klass // expected-note {{consuming use here}}
294+
end_access %5 : $*Klass
295+
%8 = begin_access [read] [static] %1 : $*Klass
296+
%9 = load [copy] %8 : $*Klass
297+
// expected-note @-1 {{consuming use here}}
298+
// expected-note @-2 {{consuming use here}}
299+
end_access %8 : $*Klass
300+
%11 = begin_access [modify] [static] %4 : $*Klass
301+
store %9 to [assign] %11 : $*Klass
302+
end_access %11 : $*Klass
303+
%14 = alloc_stack [lexical] $Klass, let, name "k2"
304+
%15 = mark_must_check [consumable_and_assignable] %14 : $*Klass
305+
%16 = begin_access [read] [static] %4 : $*Klass
306+
copy_addr %16 to [init] %15 : $*Klass
307+
end_access %16 : $*Klass
308+
%19 = load_borrow %15 : $*Klass
309+
%20 = function_ref @nonConsumingUseKlass : $@convention(thin) (@guaranteed Klass) -> ()
310+
%21 = apply %20(%19) : $@convention(thin) (@guaranteed Klass) -> ()
311+
end_borrow %19 : $Klass
312+
destroy_addr %15 : $*Klass
313+
dealloc_stack %14 : $*Klass
314+
destroy_addr %4 : $*Klass
315+
dealloc_stack %3 : $*Klass
316+
%27 = tuple ()
317+
return %27 : $()
318+
}

0 commit comments

Comments
 (0)