Skip to content

Commit c64929e

Browse files
committed
Use PrunedLivess in LoadBorrowImmutabilityChecker instead of LinearLifetimeChecker
1 parent 9f2f7f2 commit c64929e

File tree

4 files changed

+147
-20
lines changed

4 files changed

+147
-20
lines changed

lib/SIL/Verifier/LoadBorrowImmutabilityChecker.cpp

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "swift/SIL/LinearLifetimeChecker.h"
2828
#include "swift/SIL/MemAccessUtils.h"
2929
#include "swift/SIL/OwnershipUtils.h"
30+
#include "swift/SIL/PrunedLiveness.h"
3031
#include "swift/SIL/Projection.h"
3132
#include "swift/SIL/SILInstruction.h"
3233
#include "llvm/Support/Debug.h"
@@ -302,7 +303,7 @@ LoadBorrowImmutabilityAnalysis::LoadBorrowImmutabilityAnalysis(
302303

303304
// \p address may be an address, pointer, or box type.
304305
bool LoadBorrowImmutabilityAnalysis::isImmutableInScope(
305-
LoadBorrowInst *lbi, ArrayRef<Operand *> endBorrowUses,
306+
LoadBorrowInst *lbi,
306307
AccessPathWithBase accessPathWithBase) {
307308
auto accessPath = accessPathWithBase.accessPath;
308309
LinearLifetimeChecker checker(deadEndBlocks);
@@ -317,25 +318,29 @@ bool LoadBorrowImmutabilityAnalysis::isImmutableInScope(
317318
auto ownershipRoot = accessPath.getStorage().isReference()
318319
? findOwnershipReferenceRoot(accessPathWithBase.base)
319320
: SILValue();
321+
322+
BorrowedValue borrowedValue(lbi);
323+
PrunedLiveness borrowLiveness;
324+
borrowedValue.computeLiveness(borrowLiveness);
325+
320326
// Then for each write...
321327
for (auto *op : *writes) {
328+
auto *write = op->getUser();
322329
// First see if the write is a dead end block. In such a case, just skip it.
323-
if (deadEndBlocks.isDeadEnd(op->getUser()->getParent())) {
330+
if (deadEndBlocks.isDeadEnd(write->getParent())) {
324331
continue;
325332
}
326333
// A destroy_value will be a definite write only when the destroy is on the
327334
// ownershipRoot
328-
if (isa<DestroyValueInst>(op->getUser())) {
335+
if (isa<DestroyValueInst>(write)) {
329336
if (op->get() != ownershipRoot)
330337
continue;
331338
}
332-
// See if the write is within the load borrow's lifetime. If it isn't, we
333-
// don't have to worry about it.
334-
if (!checker.validateLifetime(lbi, endBorrowUses, op)) {
335-
continue;
339+
340+
if (borrowLiveness.isWithinBoundaryOfDef(write, lbi)) {
341+
llvm::errs() << "Write: " << *write;
342+
return false;
336343
}
337-
llvm::errs() << "Write: " << *op->getUser();
338-
return false;
339344
}
340345
// Ok, we are good.
341346
return true;
@@ -359,13 +364,6 @@ bool LoadBorrowImmutabilityAnalysis::isImmutable(LoadBorrowInst *lbi) {
359364
if (accessPath.getStorage().isLetAccess()) {
360365
return true;
361366
}
362-
// At this point, we know that we /may/ have writes. Now we go through various
363-
// cases to try and exhaustively identify if those writes overlap with our
364-
// load_borrow.
365-
SmallVector<Operand *, 8> endBorrowUses;
366-
visitTransitiveEndBorrows(lbi, [&](EndBorrowInst *endBorrow) {
367-
endBorrowUses.push_back(&endBorrow->getOperandRef());
368-
});
369367

370368
switch (accessPath.getStorage().getKind()) {
371369
case AccessStorage::Nested: {
@@ -380,15 +378,15 @@ bool LoadBorrowImmutabilityAnalysis::isImmutable(LoadBorrowInst *lbi) {
380378
//
381379
// TODO: As a separate analysis, verify that the load_borrow scope is always
382380
// nested within the begin_access scope (to ensure no aliasing access).
383-
return isImmutableInScope(lbi, endBorrowUses, accessPathWithBase);
381+
return isImmutableInScope(lbi, accessPathWithBase);
384382
}
385383
case AccessStorage::Argument: {
386384
auto *arg =
387385
cast<SILFunctionArgument>(accessPath.getStorage().getArgument());
388386
if (arg->hasConvention(SILArgumentConvention::Indirect_In_Guaranteed)) {
389387
return true;
390388
}
391-
return isImmutableInScope(lbi, endBorrowUses, accessPathWithBase);
389+
return isImmutableInScope(lbi, accessPathWithBase);
392390
}
393391
// FIXME: A yielded address could overlap with another in this function.
394392
case AccessStorage::Yield:
@@ -398,7 +396,7 @@ bool LoadBorrowImmutabilityAnalysis::isImmutable(LoadBorrowInst *lbi) {
398396
case AccessStorage::Tail:
399397
case AccessStorage::Global:
400398
case AccessStorage::Unidentified:
401-
return isImmutableInScope(lbi, endBorrowUses, accessPathWithBase);
399+
return isImmutableInScope(lbi, accessPathWithBase);
402400
}
403401
llvm_unreachable("Covered switch isn't covered?!");
404402
}

lib/SIL/Verifier/VerifierPrivate.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ class LoadBorrowImmutabilityAnalysis {
4040

4141
private:
4242
bool isImmutableInScope(LoadBorrowInst *lbi,
43-
ArrayRef<Operand *> endBorrowUses,
4443
AccessPathWithBase accessPathWithBase);
4544
};
4645

test/SIL/OwnershipVerifier/load_borrow_verify.sil

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ sil_stage canonical
1111
import Builtin
1212
import Swift
1313

14+
public enum FakeOptional<T> {
15+
case none
16+
case some(T)
17+
}
18+
1419
private var testGlobal: Int64
1520

1621
sil_global private @globalinit_33_00F4D2139E6BDDFEC71E5005B67B5674_token0 : $Builtin.Word
@@ -173,3 +178,82 @@ bb2(%ld : @guaranteed $K):
173178
return %6 : $()
174179
}
175180

181+
sil [ossa] @test_multiple_loadborrows : $@convention(thin) (@owned K, @owned K) -> () {
182+
bb0(%0 : @owned $K, %1 : @owned $K):
183+
%stk = alloc_stack [lexical] $K
184+
cond_br undef, bb1, bb2
185+
186+
bb1:
187+
destroy_value %1 : $K
188+
store %0 to [init] %stk : $*K
189+
br bb3
190+
191+
bb2:
192+
destroy_value %0 : $K
193+
store %1 to [init] %stk : $*K
194+
br bb3
195+
196+
bb3:
197+
cond_br undef, bb4, bb5
198+
199+
bb4:
200+
%ld1 = load_borrow %stk : $*K
201+
br bb6(%ld1 : $K)
202+
203+
bb5:
204+
%ld2 = load_borrow %stk : $*K
205+
br bb6(%ld2 : $K)
206+
207+
bb6(%ld : @guaranteed $K):
208+
%3 = function_ref @use_guaranteed : $@convention(thin) (@guaranteed K) -> ()
209+
%4 = apply %3(%ld) : $@convention(thin) (@guaranteed K) -> ()
210+
end_borrow %ld : $K
211+
destroy_addr %stk : $*K
212+
dealloc_stack %stk : $*K
213+
%6 = tuple ()
214+
return %6 : $()
215+
}
216+
217+
sil [ossa] @test_multi_bb : $@convention(thin) (@inout FakeOptional<K>) -> () {
218+
bb0(%0 : $*FakeOptional<K>):
219+
%1 = begin_access [modify] [static] %0 : $*FakeOptional<K>
220+
%2 = load_borrow %1 : $*FakeOptional<K>
221+
%4 = copy_value %2 : $FakeOptional<K>
222+
switch_enum %4 : $FakeOptional<K>, case #FakeOptional.some!enumelt: bb1, case #FakeOptional.none!enumelt: bb2
223+
224+
bb1(%6 : @owned $K):
225+
destroy_value %6 : $K
226+
end_borrow %2 : $FakeOptional<K>
227+
end_access %1 : $*FakeOptional<K>
228+
br bb3
229+
230+
bb2:
231+
unreachable
232+
233+
bb3:
234+
%12 = tuple ()
235+
return %12 : $()
236+
}
237+
238+
sil [ossa] @test_write_reborrow_loop : $@convention(thin) (@owned K) -> () {
239+
bb0(%0 : @owned $K):
240+
%stk = alloc_stack $K
241+
store %0 to [init] %stk : $*K
242+
%ld1 = load_borrow %stk : $*K
243+
br bb1(%ld1 : $K)
244+
245+
bb1(%ld2 : @guaranteed $K):
246+
cond_br undef, bb2, bb3
247+
248+
bb2:
249+
%3 = function_ref @use_guaranteed : $@convention(thin) (@guaranteed K) -> ()
250+
%4 = apply %3(%ld2) : $@convention(thin) (@guaranteed K) -> ()
251+
br bb1(%ld2 : $K)
252+
253+
bb3:
254+
end_borrow %ld2 : $K
255+
destroy_addr %stk : $*K
256+
dealloc_stack %stk : $*K
257+
%9999 = tuple()
258+
return %9999 : $()
259+
}

test/SIL/OwnershipVerifier/load_borrow_verify_errors.sil

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,49 @@ bb2(%ld : @guaranteed $Klass):
2525
%6 = tuple ()
2626
return %6 : $()
2727
}
28+
29+
// CHECK: Write: destroy_addr %2 : $*Klass
30+
// CHECK: Begin Error in function test_multiple_loadborrows
31+
// CHECK: SIL verification failed: Found load borrow that is invalidated by a local write?!: loadBorrowImmutabilityAnalysis.isImmutable(LBI)
32+
// CHECK: Verifying instruction:
33+
// CHECK: %2 = alloc_stack [lexical] $Klass
34+
// CHECK: -> %14 = load_borrow %2 : $*Klass
35+
// CHECK: br bb6(%14 : $Klass)
36+
// CHECK: End Error in function test_multiple_loadborrows
37+
sil [ossa] @test_multiple_loadborrows : $@convention(thin) (@owned Klass, @owned Klass) -> () {
38+
bb0(%0 : @owned $Klass, %1 : @owned $Klass):
39+
%stk = alloc_stack [lexical] $Klass
40+
cond_br undef, bb1, bb2
41+
42+
bb1:
43+
destroy_value %1 : $Klass
44+
store %0 to [init] %stk : $*Klass
45+
br bb3
46+
47+
bb2:
48+
destroy_value %0 : $Klass
49+
store %1 to [init] %stk : $*Klass
50+
br bb3
51+
52+
bb3:
53+
cond_br undef, bb4, bb5
54+
55+
bb4:
56+
%ld1 = load_borrow %stk : $*Klass
57+
destroy_addr %stk : $*Klass
58+
br bb6(%ld1 : $Klass)
59+
60+
bb5:
61+
%ld2 = load_borrow %stk : $*Klass
62+
destroy_addr %stk : $*Klass
63+
br bb6(%ld2 : $Klass)
64+
65+
bb6(%ld : @guaranteed $Klass):
66+
%3 = function_ref @use_guaranteed : $@convention(thin) (@guaranteed Klass) -> ()
67+
%4 = apply %3(%ld) : $@convention(thin) (@guaranteed Klass) -> ()
68+
end_borrow %ld : $Klass
69+
dealloc_stack %stk : $*Klass
70+
%6 = tuple ()
71+
return %6 : $()
72+
}
73+

0 commit comments

Comments
 (0)