Skip to content

Commit 7430c61

Browse files
committed
[SIL] Verified yielded addresses.
For yielded, nontrivial addresses, verify based on convention: - guaranteed -> must be initialized at end_apply/abort_apply - owned -> must be deinitialized at end_apply/abort_apply - inout -> must be initialized at end_apply/abort_apply
1 parent 9774e98 commit 7430c61

File tree

6 files changed

+230
-4
lines changed

6 files changed

+230
-4
lines changed

lib/SIL/Utils/MemoryLocations.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,16 @@ void MemoryLocations::analyzeLocations(SILFunction *function) {
173173
}
174174
}
175175
}
176+
if (auto *BAI = dyn_cast<BeginApplyInst>(&I)) {
177+
auto convention = BAI->getSubstCalleeConv();
178+
auto yields = convention.getYields();
179+
auto yieldedValues = BAI->getYieldedValues();
180+
for (auto index : indices(yields)) {
181+
if (convention.isSILIndirect(yields[index])) {
182+
analyzeLocation(yieldedValues[index]);
183+
}
184+
}
185+
}
176186
}
177187
}
178188
}

lib/SIL/Verifier/MemoryLifetimeVerifier.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,39 @@ void MemoryLifetimeVerifier::initDataflowInBlock(SILBasicBlock *block,
401401
}
402402
break;
403403
}
404+
case SILInstructionKind::BeginApplyInst: {
405+
auto *BAI = cast<BeginApplyInst>(&I);
406+
auto yieldedValues = BAI->getYieldedValues();
407+
for (auto index : indices(yieldedValues)) {
408+
auto fnType = BAI->getSubstCalleeType();
409+
SILArgumentConvention argConv(
410+
fnType->getYields()[index].getConvention());
411+
if (argConv.isIndirectConvention()) {
412+
genBits(state, yieldedValues[index]);
413+
}
414+
}
415+
break;
416+
}
417+
case SILInstructionKind::EndApplyInst:
418+
case SILInstructionKind::AbortApplyInst: {
419+
auto *BAI = [&]() {
420+
if (auto *EAI = dyn_cast<EndApplyInst>(&I)) {
421+
return EAI->getBeginApply();
422+
}
423+
auto *AAI = dyn_cast<AbortApplyInst>(&I);
424+
return AAI->getBeginApply();
425+
}();
426+
auto yieldedValues = BAI->getYieldedValues();
427+
for (auto index : indices(yieldedValues)) {
428+
auto fnType = BAI->getSubstCalleeType();
429+
SILArgumentConvention argConv(
430+
fnType->getYields()[index].getConvention());
431+
if (argConv.isIndirectConvention()) {
432+
killBits(state, yieldedValues[index]);
433+
}
434+
}
435+
break;
436+
}
404437
case SILInstructionKind::YieldInst: {
405438
auto *YI = cast<YieldInst>(&I);
406439
for (Operand &op : YI->getAllOperands()) {
@@ -694,6 +727,48 @@ void MemoryLifetimeVerifier::checkBlock(SILBasicBlock *block, Bits &bits) {
694727
}
695728
break;
696729
}
730+
case SILInstructionKind::BeginApplyInst: {
731+
auto *BAI = cast<BeginApplyInst>(&I);
732+
auto yieldedValues = BAI->getYieldedValues();
733+
for (auto index : indices(yieldedValues)) {
734+
auto fnType = BAI->getSubstCalleeType();
735+
SILArgumentConvention argConv(
736+
fnType->getYields()[index].getConvention());
737+
if (argConv.isIndirectConvention()) {
738+
requireBitsClear(bits, yieldedValues[index], &I);
739+
locations.setBits(bits, yieldedValues[index]);
740+
}
741+
}
742+
break;
743+
}
744+
case SILInstructionKind::EndApplyInst:
745+
case SILInstructionKind::AbortApplyInst: {
746+
auto *BAI = [&]() {
747+
if (auto *EAI = dyn_cast<EndApplyInst>(&I)) {
748+
return EAI->getBeginApply();
749+
}
750+
auto *AAI = dyn_cast<AbortApplyInst>(&I);
751+
return AAI->getBeginApply();
752+
}();
753+
auto yieldedValues = BAI->getYieldedValues();
754+
for (auto index : indices(yieldedValues)) {
755+
auto fnType = BAI->getSubstCalleeType();
756+
SILArgumentConvention argConv(
757+
fnType->getYields()[index].getConvention());
758+
if (argConv.isIndirectConvention()) {
759+
if (argConv.isInoutConvention() ||
760+
argConv.isGuaranteedConvention()) {
761+
requireBitsSet(bits | ~nonTrivialLocations, yieldedValues[index],
762+
&I);
763+
} else if (argConv.isOwnedConvention()) {
764+
requireBitsClear(bits & nonTrivialLocations, yieldedValues[index],
765+
&I);
766+
}
767+
locations.clearBits(bits, yieldedValues[index]);
768+
}
769+
}
770+
break;
771+
}
697772
case SILInstructionKind::YieldInst: {
698773
auto *YI = cast<YieldInst>(&I);
699774
for (Operand &op : YI->getAllOperands()) {

test/SIL/memory_lifetime.sil

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,3 +683,46 @@ bb3:
683683
return %14 : $()
684684
}
685685

686+
sil [ossa] @begin_apply_in_destroy : $@convention(thin) () -> () {
687+
entry:
688+
(%instance, %token) = begin_apply undef<Inner>() : $@yield_once @convention(thin) <U> () -> @yields @in U
689+
destroy_addr %instance : $*Inner
690+
end_apply %token
691+
%retval = tuple ()
692+
return %retval : $()
693+
}
694+
695+
sil [ossa] @begin_apply_in_constant_destroy : $@convention(thin) () -> () {
696+
entry:
697+
(%instance, %token) = begin_apply undef<Inner>() : $@yield_once @convention(thin) <U> () -> @yields @in_constant U
698+
destroy_addr %instance : $*Inner
699+
end_apply %token
700+
%retval = tuple ()
701+
return %retval : $()
702+
}
703+
704+
sil [ossa] @begin_apply_in_guaranteed_no_destroy : $@convention(thin) () -> () {
705+
entry:
706+
(%instance, %token) = begin_apply undef<Inner>() : $@yield_once @convention(thin) <U> () -> @yields @in_guaranteed U
707+
end_apply %token
708+
%retval = tuple ()
709+
return %retval : $()
710+
}
711+
712+
// CHECK: SIL memory lifetime failure in @begin_apply_inout_destroy: memory is not initialized, but should be
713+
sil [ossa] @begin_apply_inout_no_destroy : $@convention(thin) () -> () {
714+
entry:
715+
(%instance, %token) = begin_apply undef<Inner>() : $@yield_once @convention(thin) <U> () -> @yields @inout U
716+
end_apply %token
717+
%retval = tuple ()
718+
return %retval : $()
719+
}
720+
721+
// CHECK: SIL memory lifetime failure in @begin_apply_inout_aliasable_destroy: memory is not initialized, but should be
722+
sil [ossa] @begin_apply_inout_aliasable_no_destroy : $@convention(thin) () -> () {
723+
entry:
724+
(%instance, %token) = begin_apply undef<Inner>() : $@yield_once @convention(thin) <U> () -> @yields @inout_aliasable U
725+
end_apply %token
726+
%retval = tuple ()
727+
return %retval : $()
728+
}

test/SIL/memory_lifetime_failures.sil

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,3 +648,100 @@ bb0(%0 : $*Optional<String>):
648648
return %r : $()
649649
}
650650

651+
// CHECK: SIL memory lifetime failure in @begin_apply_in_no_destroy: memory is initialized, but shouldn't be
652+
sil [ossa] @begin_apply_in_no_destroy : $@convention(thin) () -> () {
653+
entry:
654+
(%instance, %token) = begin_apply undef<Inner>() : $@yield_once @convention(thin) <U> () -> @yields @in U
655+
end_apply %token
656+
%retval = tuple ()
657+
return %retval : $()
658+
}
659+
660+
// CHECK: SIL memory lifetime failure in @begin_apply_in_use_after_end_apply: memory is initialized, but shouldn't be
661+
sil [ossa] @begin_apply_in_use_after_end_apply : $@convention(thin) () -> () {
662+
entry:
663+
(%instance, %token) = begin_apply undef<Inner>() : $@yield_once @convention(thin) <U> () -> @yields @in U
664+
end_apply %token
665+
apply undef<Inner>(%instance) : $@convention(thin) <U> (@in U) -> ()
666+
%retval = tuple ()
667+
return %retval : $()
668+
}
669+
670+
// CHECK: SIL memory lifetime failure in @begin_apply_in_constant_no_destroy: memory is initialized, but shouldn't be
671+
sil [ossa] @begin_apply_in_constant_no_destroy : $@convention(thin) () -> () {
672+
entry:
673+
(%instance, %token) = begin_apply undef<Inner>() : $@yield_once @convention(thin) <U> () -> @yields @in_constant U
674+
end_apply %token
675+
%retval = tuple ()
676+
return %retval : $()
677+
}
678+
679+
// CHECK: SIL memory lifetime failure in @begin_apply_in_constant_use_after_end_apply: memory is initialized, but shouldn't be
680+
sil [ossa] @begin_apply_in_constant_use_after_end_apply : $@convention(thin) () -> () {
681+
entry:
682+
(%instance, %token) = begin_apply undef<Inner>() : $@yield_once @convention(thin) <U> () -> @yields @in_constant U
683+
end_apply %token
684+
apply undef<Inner>(%instance) : $@convention(thin) <U> (@in_constant U) -> ()
685+
%retval = tuple ()
686+
return %retval : $()
687+
}
688+
689+
// CHECK: SIL memory lifetime failure in @begin_apply_in_guaranteed_destroy: memory is not initialized, but should be
690+
sil [ossa] @begin_apply_in_guaranteed_destroy : $@convention(thin) () -> () {
691+
entry:
692+
(%instance, %token) = begin_apply undef<Inner>() : $@yield_once @convention(thin) <U> () -> @yields @in_guaranteed U
693+
destroy_addr %instance : $*Inner
694+
end_apply %token
695+
%retval = tuple ()
696+
return %retval : $()
697+
}
698+
699+
// CHECK: SIL memory lifetime failure in @begin_apply_in_guaranteed_use_after_end_apply: memory is not initialized, but should be
700+
sil [ossa] @begin_apply_in_guaranteed_use_after_end_apply : $@convention(thin) () -> () {
701+
entry:
702+
(%instance, %token) = begin_apply undef<Inner>() : $@yield_once @convention(thin) <U> () -> @yields @in_guaranteed U
703+
end_apply %token
704+
apply undef<Inner>(%instance) : $@convention(thin) <U> (@in_guaranteed U) -> ()
705+
%retval = tuple ()
706+
return %retval : $()
707+
}
708+
709+
// CHECK: SIL memory lifetime failure in @begin_apply_inout_destroy: memory is not initialized, but should be
710+
sil [ossa] @begin_apply_inout_destroy : $@convention(thin) () -> () {
711+
entry:
712+
(%instance, %token) = begin_apply undef<Inner>() : $@yield_once @convention(thin) <U> () -> @yields @inout U
713+
destroy_addr %instance : $*Inner
714+
end_apply %token
715+
%retval = tuple ()
716+
return %retval : $()
717+
}
718+
719+
// CHECK: SIL memory lifetime failure in @begin_apply_inout_use_after_end_apply: memory is not initialized, but should be
720+
sil [ossa] @begin_apply_inout_use_after_end_apply : $@convention(thin) () -> () {
721+
entry:
722+
(%instance, %token) = begin_apply undef<Inner>() : $@yield_once @convention(thin) <U> () -> @yields @inout U
723+
end_apply %token
724+
apply undef<Inner>(%instance) : $@convention(thin) <U> (@inout U) -> ()
725+
%retval = tuple ()
726+
return %retval : $()
727+
}
728+
729+
// CHECK: SIL memory lifetime failure in @begin_apply_inout_aliasable_destroy: memory is not initialized, but should be
730+
sil [ossa] @begin_apply_inout_aliasable_destroy : $@convention(thin) () -> () {
731+
entry:
732+
(%instance, %token) = begin_apply undef<Inner>() : $@yield_once @convention(thin) <U> () -> @yields @inout_aliasable U
733+
destroy_addr %instance : $*Inner
734+
end_apply %token
735+
%retval = tuple ()
736+
return %retval : $()
737+
}
738+
739+
// CHECK: SIL memory lifetime failure in @begin_apply_inout_aliasable_use_after_end_apply: memory is not initialized, but should be
740+
sil [ossa] @begin_apply_inout_aliasable_use_after_end_apply : $@convention(thin) () -> () {
741+
entry:
742+
(%instance, %token) = begin_apply undef<Inner>() : $@yield_once @convention(thin) <U> () -> @yields @inout_aliasable U
743+
end_apply %token
744+
apply undef<Inner>(%instance) : $@convention(thin) <U> (@inout_aliasable U) -> ()
745+
%retval = tuple ()
746+
return %retval : $()
747+
}

test/SILOptimizer/address_lowering.sil

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,8 +1506,8 @@ bb0(%0 : @guaranteed $TestGeneric<T>):
15061506
sil [ossa] @testBeginApply4YieldLoadableTrivial : $@convention(thin) () -> I {
15071507
entry:
15081508
(%instance, %token) = begin_apply undef<LoadableTrivialExtractable>() : $@yield_once @convention(thin) <U> () -> @yields @in_guaranteed U
1509-
end_apply %token
15101509
%extract = struct_extract %instance : $LoadableTrivialExtractable, #LoadableTrivialExtractable.i
1510+
end_apply %token
15111511
return %extract : $I
15121512
}
15131513

@@ -1521,9 +1521,9 @@ entry:
15211521
sil [ossa] @testBeginApply5Yield2LoadableTrivial : $@convention(thin) () -> (I, I) {
15221522
entry:
15231523
(%instance_1, %instance_2, %token) = begin_apply undef<LoadableTrivialExtractable>() : $@yield_once @convention(thin) <U> () -> (@yields @in_guaranteed U, @yields @in_guaranteed U)
1524-
end_apply %token
15251524
%i_1 = struct_extract %instance_1 : $LoadableTrivialExtractable, #LoadableTrivialExtractable.i
15261525
%i_2 = struct_extract %instance_2 : $LoadableTrivialExtractable, #LoadableTrivialExtractable.i
1526+
end_apply %token
15271527
%tuple = tuple (%i_1 : $I, %i_2 : $I)
15281528
return %tuple : $(I, I)
15291529
}
@@ -1540,8 +1540,8 @@ sil [ossa] @testBeginApply6Yield1LoadableTrivial1OpaqueGuaranteed : $@convention
15401540
entry:
15411541
(%instance_1, %instance_2, %token) = begin_apply undef<LoadableTrivialExtractable, T>() : $@yield_once @convention(thin) <U, T> () -> (@yields @in_guaranteed U, @yields @in_guaranteed T)
15421542
%copy = copy_value %instance_2 : $T
1543-
end_apply %token
15441543
%i = struct_extract %instance_1 : $LoadableTrivialExtractable, #LoadableTrivialExtractable.i
1544+
end_apply %token
15451545
%tuple = tuple (%i : $I, %copy : $T)
15461546
return %tuple : $(I, T)
15471547
}
@@ -1654,12 +1654,12 @@ entry:
16541654
sil [ossa] @testBeginApplyCYield1LoadableNontrivialOwned1OpaqueOwned : $@convention(thin) <T> () -> (@out Klass, @out T) {
16551655
entry:
16561656
(%instance_1, %instance_2, %token) = begin_apply undef<LoadableNontrivial, T>() : $@yield_once @convention(thin) <U, T> () -> (@yields @in U, @yields @in T)
1657-
end_apply %token
16581657
%borrow_1 = begin_borrow %instance_1 : $LoadableNontrivial
16591658
%extract_1 = struct_extract %borrow_1 : $LoadableNontrivial, #LoadableNontrivial.x
16601659
%copy_1 = copy_value %extract_1 : $Klass
16611660
end_borrow %borrow_1 : $LoadableNontrivial
16621661
destroy_value %instance_1 : $LoadableNontrivial
1662+
end_apply %token
16631663
%retval = tuple (%copy_1 : $Klass, %instance_2 : $T)
16641664
return %retval : $(Klass, T)
16651665
}

test/SILOptimizer/specialize_ossa.sil

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,7 @@ bb0(%0 : $Builtin.Int32, %1 : @guaranteed $Builtin.NativeObject):
12521252
%1c = copy_value %1 : $Builtin.NativeObject
12531253
store %1c to [init] %1b : $*Builtin.NativeObject
12541254
(%1result, %1token) = begin_apply %f<Builtin.NativeObject>(%1b) : $@yield_once @convention(thin) <T> (@in_guaranteed T) -> @yields @in T
1255+
destroy_addr %1result : $*Builtin.NativeObject
12551256

12561257
end_apply %1token
12571258
destroy_addr %1b : $*Builtin.NativeObject

0 commit comments

Comments
 (0)