Skip to content

Commit 6a6d3f0

Browse files
committed
SIL: handle mark_dependence in the MemoryLifetimeVerifier
The check is not perfect, because it only checks that the base operand is alive _at_ the mark_dependence. Ideally it should check that the base operand is alive during the whole lifetime of the value operand.
1 parent a88cb49 commit 6a6d3f0

File tree

6 files changed

+93
-3
lines changed

6 files changed

+93
-3
lines changed

lib/SIL/Utils/MemoryLocations.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ static SILValue getBaseValue(SILValue addr) {
8989
case ValueKind::BeginAccessInst:
9090
addr = cast<BeginAccessInst>(addr)->getOperand();
9191
break;
92+
case ValueKind::MarkDependenceInst:
93+
addr = cast<MarkDependenceInst>(addr)->getValue();
94+
break;
9295
default:
9396
return addr;
9497
}
@@ -334,6 +337,14 @@ bool MemoryLocations::analyzeLocationUsesRecursively(SILValue V, unsigned locIdx
334337
if (!cast<LoadBorrowInst>(user)->getUsersOfType<BranchInst>().empty())
335338
return false;
336339
break;
340+
case SILInstructionKind::MarkDependenceInst: {
341+
auto *mdi = cast<MarkDependenceInst>(user);
342+
if (use == &mdi->getAllOperands()[MarkDependenceInst::Value]) {
343+
if (!analyzeLocationUsesRecursively(mdi, locIdx, collectedVals, subLocationMap))
344+
return false;
345+
}
346+
break;
347+
}
337348
case SILInstructionKind::DebugValueInst:
338349
if (cast<DebugValueInst>(user)->hasAddrVal())
339350
break;

lib/SIL/Verifier/MemoryLifetimeVerifier.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,10 @@ void MemoryLifetimeVerifier::requireBitsSetForArgument(const Bits &bits, Operand
306306
}
307307

308308
bool MemoryLifetimeVerifier::applyMayRead(Operand *argOp, SILValue addr) {
309+
// Conservatively assume that a partial_apply does _not_ read an argument.
310+
if (isa<PartialApplyInst>(argOp->getUser()))
311+
return false;
312+
309313
FullApplySite as(argOp->getUser());
310314
CalleeList callees;
311315
if (calleeCache) {
@@ -881,6 +885,18 @@ void MemoryLifetimeVerifier::checkBlock(SILBasicBlock *block, Bits &bits) {
881885
locations.clearBits(bits, opVal);
882886
break;
883887
}
888+
case SILInstructionKind::MarkDependenceInst: {
889+
auto *mdi = cast<MarkDependenceInst>(&I);
890+
if (mdi->getBase()->getType().isAddress() &&
891+
// In case the mark_dependence is used for a closure it might be that the base
892+
// is "self" in an initializer and "self" is not fully initialized, yet.
893+
!mdi->getType().isFunction()) {
894+
requireBitsSet(bits, mdi->getBase(), &I);
895+
}
896+
// TODO: check that the base operand is alive during the whole lifetime
897+
// of the value operand.
898+
break;
899+
}
884900
default:
885901
break;
886902
}

test/SIL/memory_lifetime.sil

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,18 @@ bb0(%0 : $*T):
510510
return %res : $()
511511
}
512512

513+
sil [ossa] @test_non_escaping_closure_with_mark_dependence : $@convention(thin) () -> () {
514+
bb0:
515+
%0 = alloc_stack $T
516+
%func = function_ref @closure : $@convention(thin) (@in_guaranteed T) -> ()
517+
%pa = partial_apply [callee_guaranteed] [on_stack] %func(%0) : $@convention(thin) (@in_guaranteed T) -> ()
518+
%pa1 = mark_dependence %pa on %0
519+
destroy_value %pa1 : $@noescape @callee_guaranteed () -> ()
520+
dealloc_stack %0
521+
%res = tuple ()
522+
return %res : $()
523+
}
524+
513525
sil [ossa] @test_store_borrow : $@convention(thin) (@guaranteed T) -> () {
514526
bb0(%0 : @guaranteed $T):
515527
%s = alloc_stack $T
@@ -806,3 +818,35 @@ bb3:
806818
return %r : $()
807819
}
808820

821+
sil [ossa] @mark_dependence : $@convention(method) (@owned T, @owned Inner) -> @owned Inner {
822+
bb0(%0 : @owned $T, %1 : @owned $Inner):
823+
%2 = alloc_stack $T
824+
store %0 to [init] %2
825+
%4 = alloc_stack $Inner
826+
store %1 to [init] %4
827+
%6 = mark_dependence %4 on %2
828+
%7 = begin_access [read] [dynamic] %6
829+
%8 = load [take] %7
830+
end_access %7
831+
dealloc_stack %4
832+
destroy_addr %2
833+
dealloc_stack %2
834+
return %8
835+
}
836+
837+
sil [ossa] @mark_dependence_uninit_value : $@convention(thin) (@owned T, @owned Inner) -> @owned Inner {
838+
bb0(%0 : @owned $T, %1 : @owned $Inner):
839+
%2 = alloc_stack $T
840+
store %0 to [init] %2
841+
%4 = alloc_stack $Inner
842+
store %1 to [init] %4
843+
%6 = mark_dependence %4 on %2
844+
%7 = begin_access [read] [dynamic] %6
845+
%8 = load [take] %7
846+
end_access %7
847+
dealloc_stack %4
848+
destroy_addr %2
849+
dealloc_stack %2
850+
return %8
851+
}
852+

test/SIL/memory_lifetime_failures.sil

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,3 +792,20 @@ bb0(%0: $*Inner):
792792
return %r : $()
793793
}
794794

795+
// CHECK: SIL memory lifetime failure in @mark_dependence_uninit_base: memory is not initialized, but should be
796+
sil [ossa] @mark_dependence_uninit_base : $@convention(thin) (@owned T, @owned Inner) -> @owned Inner {
797+
bb0(%0 : @owned $T, %1 : @owned $Inner):
798+
%2 = alloc_stack $T
799+
store %0 to [init] %2
800+
%4 = alloc_stack $Inner
801+
store %1 to [init] %4
802+
destroy_addr %2
803+
%6 = mark_dependence %4 on %2
804+
%7 = begin_access [read] [dynamic] %6
805+
%8 = load [take] %7
806+
end_access %7
807+
dealloc_stack %4
808+
dealloc_stack %2
809+
return %8
810+
}
811+

test/SILOptimizer/ossa_lifetime_completion.sil

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,18 +216,20 @@ bb3:
216216
}
217217

218218
// Ensure no assert fires while handling lifetime end of partial_apply
219-
sil [ossa] @testPartialApplyStack2 : $@convention(thin) (@guaranteed C) -> () {
220-
bb0(%0 : @guaranteed $C):
219+
sil [ossa] @testPartialApplyStack2 : $@convention(thin) (@owned C) -> () {
220+
bb0(%0 : @owned $C):
221221
specify_test "ossa_lifetime_completion @instruction[1] availability"
222222
%2 = alloc_stack $C
223223
%3 = copy_value %0 : $C
224+
store %0 to [init] %2
224225
%4 = begin_borrow %3 : $C
225226
%5 = function_ref @foo : $@convention(thin) (@guaranteed C) -> ()
226227
%6 = partial_apply [callee_guaranteed] [on_stack] %5(%4) : $@convention(thin) (@guaranteed C) -> ()
227228
%7 = mark_dependence %6 : $@noescape @callee_guaranteed () -> () on %2 : $*C
228229
destroy_value %7 : $@noescape @callee_guaranteed () -> ()
229230
end_borrow %4 : $C
230231
destroy_value %3 : $C
232+
destroy_addr %2 : $*C
231233
dealloc_stack %2 : $*C
232234
%12 = tuple ()
233235
return %12 : $()

test/SILOptimizer/redundant_load_elim_nontrivial_ossa.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,7 @@ bb0(%0 : $*Klass, %1 : $*Klass, %2 : @owned $Klass):
812812
// CHECK: load
813813
// CHECK-NOT: load
814814
// CHECK-LABEL: } // end sil function 'redundant_load_mark_dependence'
815-
sil [ossa] @redundant_load_mark_dependence : $@convention(thin) (@inout Klass, @guaranteed Builtin.NativeObject) -> @owned (Klass, Klass) {
815+
sil [ossa] @redundant_load_mark_dependence : $@convention(thin) (@in Klass, @guaranteed Builtin.NativeObject) -> @owned (Klass, Klass) {
816816
bb0(%0 : $*Klass, %1 : @guaranteed $Builtin.NativeObject):
817817
%2 = mark_dependence %0 : $*Klass on %1 : $Builtin.NativeObject
818818
%4 = load [copy] %2 : $*Klass

0 commit comments

Comments
 (0)