Skip to content

Commit c4ed7bd

Browse files
authored
Merge pull request swiftlang#30249 from gottesmm/pr-1c59567660f53bc35248d864c54d25e2760a74d0
[ownership] Implement InteriorPointer abstraction/validate current recognized ones addresses are not used outside of parent object's borrowed lifetime.
2 parents 7942cbf + 0aec5a7 commit c4ed7bd

File tree

4 files changed

+272
-14
lines changed

4 files changed

+272
-14
lines changed

include/swift/SIL/OwnershipUtils.h

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,8 @@ struct BorrowScopeIntroducingValueKind {
264264
llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
265265
BorrowScopeIntroducingValueKind kind);
266266

267+
struct InteriorPointerOperand;
268+
267269
/// A higher level construct for working with values that represent the
268270
/// introduction of a new borrow scope.
269271
///
@@ -337,6 +339,15 @@ struct BorrowScopeIntroducingValue {
337339
void print(llvm::raw_ostream &os) const;
338340
SWIFT_DEBUG_DUMP { print(llvm::dbgs()); }
339341

342+
/// Visit each of the interior pointer uses of this underlying borrow
343+
/// introduced value. These object -> address projections and any transitive
344+
/// address uses must be treated as liveness requiring uses of the guaranteed
345+
/// value and we can not shrink the scope beyond that point. Returns true if
346+
/// we were able to understand all uses and thus guarantee we found all
347+
/// interior pointer uses. Returns false otherwise.
348+
bool visitInteriorPointerOperands(
349+
function_ref<void(const InteriorPointerOperand &)> func) const;
350+
340351
private:
341352
/// Internal constructor for failable static constructor. Please do not expand
342353
/// its usage since it assumes the code passed in is well formed.
@@ -375,6 +386,87 @@ getSingleBorrowIntroducingValue(SILValue value);
375386
Optional<BorrowScopeIntroducingValue>
376387
getSingleBorrowIntroducingValue(SILValue inputValue);
377388

389+
struct InteriorPointerOperandKind {
390+
using UnderlyingKindTy = std::underlying_type<SILInstructionKind>::type;
391+
392+
enum Kind : UnderlyingKindTy {
393+
RefElementAddr = UnderlyingKindTy(SILInstructionKind::RefElementAddrInst),
394+
RefTailAddr = UnderlyingKindTy(SILInstructionKind::RefTailAddrInst),
395+
};
396+
397+
Kind value;
398+
399+
InteriorPointerOperandKind(Kind newValue) : value(newValue) {}
400+
InteriorPointerOperandKind(const InteriorPointerOperandKind &other)
401+
: value(other.value) {}
402+
operator Kind() const { return value; }
403+
404+
static Optional<InteriorPointerOperandKind> get(Operand *use) {
405+
switch (use->getUser()->getKind()) {
406+
default:
407+
return None;
408+
case SILInstructionKind::RefElementAddrInst:
409+
return InteriorPointerOperandKind(RefElementAddr);
410+
case SILInstructionKind::RefTailAddrInst:
411+
return InteriorPointerOperandKind(RefTailAddr);
412+
}
413+
}
414+
415+
void print(llvm::raw_ostream &os) const;
416+
SWIFT_DEBUG_DUMP;
417+
};
418+
419+
/// A mixed object->address projection that projects a memory location out of an
420+
/// object with guaranteed ownership. All transitive address uses of the
421+
/// interior pointer must be within the lifetime of the guaranteed lifetime. As
422+
/// such, these must be treated as implicit uses of the parent guaranteed value.
423+
struct InteriorPointerOperand {
424+
Operand *operand;
425+
InteriorPointerOperandKind kind;
426+
427+
InteriorPointerOperand(Operand *op)
428+
: operand(op), kind(*InteriorPointerOperandKind::get(op)) {}
429+
430+
/// If value is a borrow introducer return it after doing some checks.
431+
static Optional<InteriorPointerOperand> get(Operand *op) {
432+
auto kind = InteriorPointerOperandKind::get(op);
433+
if (!kind)
434+
return None;
435+
return InteriorPointerOperand(op, *kind);
436+
}
437+
438+
/// Return the end scope of all borrow introducers of the parent value of this
439+
/// projection. Returns true if we were able to find all borrow introducing
440+
/// values.
441+
bool visitBaseValueScopeEndingUses(function_ref<void(Operand *)> func) const {
442+
SmallVector<BorrowScopeIntroducingValue, 4> introducers;
443+
if (!getAllBorrowIntroducingValues(operand->get(), introducers))
444+
return false;
445+
for (const auto &introducer : introducers) {
446+
if (!introducer.isLocalScope())
447+
continue;
448+
introducer.visitLocalScopeEndingUses(func);
449+
}
450+
return true;
451+
}
452+
453+
SILValue getProjectedAddress() const {
454+
switch (kind) {
455+
case InteriorPointerOperandKind::RefElementAddr:
456+
return cast<RefElementAddrInst>(operand->getUser());
457+
case InteriorPointerOperandKind::RefTailAddr:
458+
return cast<RefTailAddrInst>(operand->getUser());
459+
}
460+
llvm_unreachable("Covered switch isn't covered?!");
461+
}
462+
463+
private:
464+
/// Internal constructor for failable static constructor. Please do not expand
465+
/// its usage since it assumes the code passed in is well formed.
466+
InteriorPointerOperand(Operand *op, InteriorPointerOperandKind kind)
467+
: operand(op), kind(kind) {}
468+
};
469+
378470
} // namespace swift
379471

380472
#endif

lib/SIL/OwnershipUtils.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "swift/SIL/OwnershipUtils.h"
1414
#include "swift/Basic/Defer.h"
1515
#include "swift/SIL/LinearLifetimeChecker.h"
16+
#include "swift/SIL/Projection.h"
1617
#include "swift/SIL/SILArgument.h"
1718
#include "swift/SIL/SILInstruction.h"
1819

@@ -397,6 +398,51 @@ bool BorrowScopeIntroducingValue::visitLocalScopeTransitiveEndingUses(
397398
return foundError;
398399
}
399400

401+
bool BorrowScopeIntroducingValue::visitInteriorPointerOperands(
402+
function_ref<void(const InteriorPointerOperand &)> func) const {
403+
SmallVector<Operand *, 32> worklist(value->getUses());
404+
while (!worklist.empty()) {
405+
auto *op = worklist.pop_back_val();
406+
407+
if (auto interiorPointer = InteriorPointerOperand::get(op)) {
408+
func(*interiorPointer);
409+
continue;
410+
}
411+
412+
auto *user = op->getUser();
413+
if (isa<BeginBorrowInst>(user) || isa<DebugValueInst>(user) ||
414+
isa<SuperMethodInst>(user) || isa<ClassMethodInst>(user) ||
415+
isa<CopyValueInst>(user) || isa<EndBorrowInst>(user) ||
416+
isa<ApplyInst>(user) || isa<StoreBorrowInst>(user) ||
417+
isa<StoreInst>(user) || isa<PartialApplyInst>(user) ||
418+
isa<UnmanagedRetainValueInst>(user) ||
419+
isa<UnmanagedReleaseValueInst>(user) ||
420+
isa<UnmanagedAutoreleaseValueInst>(user)) {
421+
continue;
422+
}
423+
424+
// These are interior pointers that have not had support yet added for them.
425+
if (isa<OpenExistentialBoxInst>(user) ||
426+
isa<ProjectExistentialBoxInst>(user)) {
427+
continue;
428+
}
429+
430+
// Look through object.
431+
if (auto *svi = dyn_cast<SingleValueInstruction>(user)) {
432+
if (Projection::isObjectProjection(svi)) {
433+
for (SILValue result : user->getResults()) {
434+
llvm::copy(result->getUses(), std::back_inserter(worklist));
435+
}
436+
continue;
437+
}
438+
}
439+
440+
return false;
441+
}
442+
443+
return true;
444+
}
445+
400446
//===----------------------------------------------------------------------===//
401447
// Introducer Searching Routines
402448
//===----------------------------------------------------------------------===//

lib/SIL/SILOwnershipVerifier.cpp

Lines changed: 130 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/SIL/BasicBlockUtils.h"
2525
#include "swift/SIL/Dominance.h"
2626
#include "swift/SIL/DynamicCasts.h"
27+
#include "swift/SIL/InstructionUtils.h"
2728
#include "swift/SIL/LinearLifetimeChecker.h"
2829
#include "swift/SIL/OwnershipUtils.h"
2930
#include "swift/SIL/PrettyStackTrace.h"
@@ -164,10 +165,12 @@ class SILValueOwnershipChecker {
164165
bool isSubobjectProjectionWithLifetimeEndingUses(
165166
SILValue value,
166167
const SmallVectorImpl<Operand *> &lifetimeEndingUsers) const;
167-
bool
168-
discoverImplicitRegularUsers(const BorrowScopeOperand &initialScopedOperand,
169-
SmallVectorImpl<Operand *> &implicitRegularUsers,
170-
bool isGuaranteed);
168+
bool discoverBorrowOperandImplicitRegularUsers(
169+
const BorrowScopeOperand &initialScopedOperand,
170+
SmallVectorImpl<Operand *> &implicitRegularUsers, bool isGuaranteed);
171+
bool discoverInteriorPointerOperandImplicitRegularUsers(
172+
const InteriorPointerOperand &interiorPointerOperand,
173+
SmallVectorImpl<Operand *> &implicitRegularUsers);
171174
};
172175

173176
} // end anonymous namespace
@@ -240,7 +243,7 @@ bool SILValueOwnershipChecker::isCompatibleDefUse(
240243
}
241244

242245
/// Returns true if an error was found.
243-
bool SILValueOwnershipChecker::discoverImplicitRegularUsers(
246+
bool SILValueOwnershipChecker::discoverBorrowOperandImplicitRegularUsers(
244247
const BorrowScopeOperand &initialScopedOperand,
245248
SmallVectorImpl<Operand *> &implicitRegularUsers, bool isGuaranteed) {
246249
if (!initialScopedOperand.areAnyUserResultsBorrowIntroducers()) {
@@ -288,6 +291,115 @@ bool SILValueOwnershipChecker::discoverImplicitRegularUsers(
288291
return foundError;
289292
}
290293

294+
bool SILValueOwnershipChecker::
295+
discoverInteriorPointerOperandImplicitRegularUsers(
296+
const InteriorPointerOperand &interiorPointerOperand,
297+
SmallVectorImpl<Operand *> &implicitRegularUsers) {
298+
SILValue projectedAddress = interiorPointerOperand.getProjectedAddress();
299+
SmallVector<Operand *, 8> worklist(projectedAddress->getUses());
300+
301+
bool foundError = false;
302+
303+
while (!worklist.empty()) {
304+
auto *op = worklist.pop_back_val();
305+
306+
// Skip type dependent operands.
307+
if (op->isTypeDependent())
308+
continue;
309+
310+
// Before we do anything, add this operand to our implicit regular user
311+
// list.
312+
implicitRegularUsers.push_back(op);
313+
314+
// Then update the worklist with new things to find if we recognize this
315+
// inst and then continue. If we fail, we emit an error at the bottom of the
316+
// loop that we didn't recognize the user.
317+
auto *user = op->getUser();
318+
319+
// First, eliminate "end point uses" that we just need to check liveness at
320+
// and do not need to check transitive uses of.
321+
if (isa<LoadInst>(user) || isa<CopyAddrInst>(user) ||
322+
isIncidentalUse(user) || isa<StoreInst>(user) ||
323+
isa<StoreBorrowInst>(user) || isa<PartialApplyInst>(user) ||
324+
isa<DestroyAddrInst>(user) || isa<AssignInst>(user) ||
325+
isa<AddressToPointerInst>(user) || isa<YieldInst>(user) ||
326+
isa<LoadUnownedInst>(user) || isa<StoreUnownedInst>(user) ||
327+
isa<EndApplyInst>(user) || isa<LoadWeakInst>(user) ||
328+
isa<StoreWeakInst>(user) || isa<AssignByWrapperInst>(user) ||
329+
isa<BeginUnpairedAccessInst>(user) ||
330+
isa<EndUnpairedAccessInst>(user) || isa<WitnessMethodInst>(user) ||
331+
isa<SwitchEnumAddrInst>(user) || isa<CheckedCastAddrBranchInst>(user) ||
332+
isa<SelectEnumAddrInst>(user)) {
333+
continue;
334+
}
335+
336+
// Then handle users that we need to look at transitive uses of.
337+
if (Projection::isAddressProjection(user) ||
338+
isa<ProjectBlockStorageInst>(user) ||
339+
isa<OpenExistentialAddrInst>(user) ||
340+
isa<InitExistentialAddrInst>(user) || isa<BeginAccessInst>(user) ||
341+
isa<TailAddrInst>(user) || isa<IndexAddrInst>(user)) {
342+
for (SILValue r : user->getResults()) {
343+
llvm::copy(r->getUses(), std::back_inserter(worklist));
344+
}
345+
continue;
346+
}
347+
348+
if (auto *builtin = dyn_cast<BuiltinInst>(user)) {
349+
if (auto kind = builtin->getBuiltinKind()) {
350+
if (*kind == BuiltinValueKind::TSanInoutAccess) {
351+
continue;
352+
}
353+
}
354+
}
355+
356+
// If we have a load_borrow, add it's end scope to the liveness requirement.
357+
if (auto *lbi = dyn_cast<LoadBorrowInst>(user)) {
358+
transform(lbi->getEndBorrows(), std::back_inserter(implicitRegularUsers),
359+
[](EndBorrowInst *ebi) { return &ebi->getAllOperands()[0]; });
360+
continue;
361+
}
362+
363+
// TODO: Merge this into the full apply site code below.
364+
if (auto *beginApply = dyn_cast<BeginApplyInst>(user)) {
365+
// TODO: Just add this to implicit regular user list?
366+
llvm::copy(beginApply->getTokenResult()->getUses(),
367+
std::back_inserter(implicitRegularUsers));
368+
continue;
369+
}
370+
371+
if (auto fas = FullApplySite::isa(user)) {
372+
continue;
373+
}
374+
375+
if (auto *mdi = dyn_cast<MarkDependenceInst>(user)) {
376+
// If this is the base, just treat it as a liveness use.
377+
if (op->get() == mdi->getBase()) {
378+
continue;
379+
}
380+
381+
// If we are the value use, look through it.
382+
llvm::copy(mdi->getValue()->getUses(), std::back_inserter(worklist));
383+
continue;
384+
}
385+
386+
// We were unable to recognize this user, so return true that we failed.
387+
handleError([&] {
388+
llvm::errs()
389+
<< "Function: " << op->getUser()->getFunction()->getName() << "\n"
390+
<< "Could not recognize address user of interior pointer operand!\n"
391+
<< "Interior Pointer Operand: "
392+
<< *interiorPointerOperand.operand->getUser()
393+
<< "Address User: " << *op->getUser();
394+
});
395+
foundError = true;
396+
}
397+
398+
// We were able to recognize all of the uses of the address, so return false
399+
// that we did not find any errors.
400+
return foundError;
401+
}
402+
291403
bool SILValueOwnershipChecker::gatherNonGuaranteedUsers(
292404
SmallVectorImpl<Operand *> &lifetimeEndingUsers,
293405
SmallVectorImpl<Operand *> &nonLifetimeEndingUsers,
@@ -348,8 +460,8 @@ bool SILValueOwnershipChecker::gatherNonGuaranteedUsers(
348460
// initial end scope instructions without any further work.
349461
//
350462
// Maybe: Is borrow scope non-local?
351-
foundError |= discoverImplicitRegularUsers(*initialScopedOperand,
352-
implicitRegularUsers, false);
463+
foundError |= discoverBorrowOperandImplicitRegularUsers(
464+
*initialScopedOperand, implicitRegularUsers, false);
353465
}
354466

355467
return foundError;
@@ -441,11 +553,19 @@ bool SILValueOwnershipChecker::gatherUsers(
441553
if (auto scopedOperand = BorrowScopeOperand::get(op)) {
442554
assert(!scopedOperand->consumesGuaranteedValues());
443555

444-
foundError |= discoverImplicitRegularUsers(*scopedOperand,
445-
implicitRegularUsers, true);
556+
foundError |= discoverBorrowOperandImplicitRegularUsers(
557+
*scopedOperand, implicitRegularUsers, true);
558+
}
559+
560+
// Next see if our use is an interior pointer operand. If we have an
561+
// interior pointer, we need to add all of its address uses as "implicit
562+
// regular users" of our consumed value.
563+
if (auto interiorPointerOperand = InteriorPointerOperand::get(op)) {
564+
foundError |= discoverInteriorPointerOperandImplicitRegularUsers(
565+
*interiorPointerOperand, implicitRegularUsers);
446566
}
447567

448-
// And then add the op to the non lifetime ending user list.
568+
// Finally add the op to the non lifetime ending user list.
449569
LLVM_DEBUG(llvm::dbgs() << " Regular User: " << *user);
450570
nonLifetimeEndingUsers.push_back(op);
451571
continue;

test/SILOptimizer/mem-behavior-all.sil

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ class Parent {
1919
// CHECK-LABEL: @testLetSideEffects
2020
//
2121
// The store does not affect the let-load (ref_element_addr).
22-
// CHECK: PAIR #35.
23-
// CHECK-NEXT: %7 = begin_access [modify] [static] %1 : $*Builtin.Int32
24-
// CHECK-NEXT: %11 = ref_element_addr %10 : $C, #C.prop
22+
// CHECK: PAIR #25.
23+
// CHECK-NEXT: %6 = begin_access [modify] [static] %1 : $*Builtin.Int32
24+
// CHECK-NEXT: %10 = ref_element_addr %9 : $C, #C.prop
2525
// CHECK-NEXT: r=0,w=0,se=0
2626
//
2727
// Any unknown instructions with side effects does affect the let-load.
@@ -38,7 +38,6 @@ bb0(%0 : @owned $Parent, %1 : $*Builtin.Int32):
3838
%borrow1 = begin_borrow %0 : $Parent
3939
%childAdr = ref_element_addr %borrow1 : $Parent, #Parent.child
4040
%child = load_borrow %childAdr : $*C
41-
end_borrow %borrow1 : $Parent
4241

4342
%three = integer_literal $Builtin.Int32, 3
4443
%access = begin_access [modify] [static] %1 : $*Builtin.Int32
@@ -50,6 +49,7 @@ bb0(%0 : @owned $Parent, %1 : $*Builtin.Int32):
5049
%val = load [trivial] %propAdr : $*Builtin.Int32
5150
end_borrow %borrow2 : $C
5251
end_borrow %child : $C
52+
end_borrow %borrow1 : $Parent
5353
destroy_value %0 : $Parent
5454
return %val : $Builtin.Int32
5555
}

0 commit comments

Comments
 (0)