Skip to content

Commit 5244e6f

Browse files
committed
Improve verification for store_borrow
Add ScopedAddressOperand and ScopedAddressValue abstraction utilities Introduce verification for store_borrow to validate its uses are correctly enclosed in their scope. Include end_borrow/end_access as implicit uses while validating a borrow introducer Add flow sensitive verifier rule for store_borrow/end_borrow pair Make sure store_borrow is always to an alloc_stack Make sure uses to store borrow location are via its return address only
1 parent 196994f commit 5244e6f

11 files changed

+661
-44
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//===--- ScopedAddressUtils.h ---------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_SIL_SCOPEDADDRESSUTILS_H
14+
#define SWIFT_SIL_SCOPEDADDRESSUTILS_H
15+
16+
#include "swift/Basic/Debug.h"
17+
#include "swift/Basic/LLVM.h"
18+
#include "swift/SIL/PrunedLiveness.h"
19+
#include "swift/SIL/SILBasicBlock.h"
20+
#include "swift/SIL/SILInstruction.h"
21+
#include "swift/SIL/SILValue.h"
22+
23+
namespace swift {
24+
25+
class ScopedAddressValueKind {
26+
public:
27+
enum Kind : uint8_t {
28+
Invalid = 0,
29+
StoreBorrow,
30+
BeginAccess,
31+
};
32+
33+
private:
34+
Kind value;
35+
36+
public:
37+
static ScopedAddressValueKind get(SILValue value) {
38+
switch (value->getKind()) {
39+
default:
40+
return Kind::Invalid;
41+
case ValueKind::StoreBorrowInst:
42+
return Kind::StoreBorrow;
43+
case ValueKind::BeginAccessInst:
44+
return Kind::BeginAccess;
45+
}
46+
}
47+
48+
ScopedAddressValueKind(Kind newValue) : value(newValue) {}
49+
50+
operator Kind() const { return value; }
51+
52+
void print(llvm::raw_ostream &os) const;
53+
SWIFT_DEBUG_DUMP { print(llvm::dbgs()); }
54+
};
55+
56+
llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
57+
ScopedAddressValueKind kind);
58+
59+
struct ScopedAddressValue {
60+
SILValue value;
61+
ScopedAddressValueKind kind = ScopedAddressValueKind::Invalid;
62+
63+
ScopedAddressValue() = default;
64+
65+
explicit ScopedAddressValue(SILValue value) {
66+
kind = ScopedAddressValueKind::get(value);
67+
if (kind)
68+
this->value = value;
69+
}
70+
71+
operator bool() const {
72+
return kind != ScopedAddressValueKind::Invalid && value;
73+
}
74+
75+
void print(llvm::raw_ostream &os) const;
76+
SWIFT_DEBUG_DUMP { print(llvm::dbgs()); }
77+
78+
// Helpers to allow a ScopedAddressValue to easily be used as a SILValue
79+
// programatically.
80+
SILValue operator->() { return value; }
81+
SILValue operator->() const { return value; }
82+
SILValue operator*() { return value; }
83+
SILValue operator*() const { return value; }
84+
85+
/// Returns true if \p op is a scope edning use of the scoped address value.
86+
bool isScopeEndingUse(Operand *op) const;
87+
/// Pass all scope ending instructions to the visitor.
88+
bool visitScopeEndingUses(function_ref<bool(Operand *)> visitor) const;
89+
/// Returns false, if liveness cannot be computed due to pointer escape or
90+
/// unkown address use. Add this scope's live blocks into the PrunedLiveness
91+
/// result.
92+
bool computeLiveness(PrunedLiveness &liveness) const;
93+
};
94+
95+
llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
96+
const ScopedAddressValue &value);
97+
98+
/// Returns true if there are other store_borrows enclosed within a store_borrow
99+
/// \p sbi's scope
100+
bool hasOtherStoreBorrowsInLifetime(StoreBorrowInst *sbi,
101+
PrunedLiveness *liveness,
102+
DeadEndBlocks *deadEndBlocks);
103+
} // namespace swift
104+
105+
#endif

lib/SIL/Utils/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ target_sources(swiftSIL PRIVATE
1414
PrettyStackTrace.cpp
1515
Projection.cpp
1616
PrunedLiveness.cpp
17+
ScopedAddressUtils.cpp
1718
SILBridging.cpp
1819
SILInstructionWorklist.cpp
1920
SILRemarkStreamer.cpp

lib/SIL/Utils/OwnershipUtils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/SIL/Projection.h"
2121
#include "swift/SIL/PrunedLiveness.h"
2222
#include "swift/SIL/SILArgument.h"
23+
#include "swift/SIL/SILBuilder.h"
2324
#include "swift/SIL/SILInstruction.h"
2425

2526
using namespace swift;

lib/SIL/Utils/ScopedAddressUtils.cpp

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
//===--- ScopedAddressUtils.cpp -------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "swift/SIL/ScopedAddressUtils.h"
14+
#include "swift/SIL/OwnershipUtils.h"
15+
#include "swift/SIL/PrunedLiveness.h"
16+
#include "swift/SIL/SILArgument.h"
17+
#include "swift/SIL/SILBuilder.h"
18+
#include "swift/SIL/SILInstruction.h"
19+
20+
using namespace swift;
21+
22+
void ScopedAddressValueKind::print(llvm::raw_ostream &os) const {
23+
switch (value) {
24+
case ScopedAddressValueKind::Invalid:
25+
llvm_unreachable("Using invalid case?!");
26+
case ScopedAddressValueKind::StoreBorrow:
27+
os << "StoreBorrow";
28+
return;
29+
case ScopedAddressValueKind::BeginAccess:
30+
os << "BeginAccess";
31+
return;
32+
}
33+
llvm_unreachable("Covered switch isn't covered?!");
34+
}
35+
36+
llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os,
37+
ScopedAddressValueKind kind) {
38+
kind.print(os);
39+
return os;
40+
}
41+
42+
bool ScopedAddressValue::isScopeEndingUse(Operand *op) const {
43+
switch (kind) {
44+
case ScopedAddressValueKind::Invalid:
45+
llvm_unreachable("Using invalid case?!");
46+
case ScopedAddressValueKind::StoreBorrow: {
47+
if (auto *endBorrow = dyn_cast<EndBorrowInst>(op->getUser())) {
48+
return endBorrow->getOperand() == value;
49+
}
50+
return false;
51+
}
52+
case ScopedAddressValueKind::BeginAccess: {
53+
if (auto *endAccess = dyn_cast<EndAccessInst>(op->getUser())) {
54+
return endAccess->getOperand() == value;
55+
}
56+
return false;
57+
}
58+
}
59+
}
60+
61+
bool ScopedAddressValue::visitScopeEndingUses(
62+
function_ref<bool(Operand *)> visitor) const {
63+
switch (kind) {
64+
case ScopedAddressValueKind::Invalid:
65+
llvm_unreachable("Using invalid case?!");
66+
case ScopedAddressValueKind::StoreBorrow: {
67+
for (auto *use : value->getUses()) {
68+
if (isa<EndBorrowInst>(use->getUser())) {
69+
if (!visitor(use))
70+
return false;
71+
}
72+
}
73+
return true;
74+
}
75+
case ScopedAddressValueKind::BeginAccess: {
76+
for (auto *use : value->getUses()) {
77+
if (isa<EndAccessInst>(use->getUser())) {
78+
if (!visitor(use))
79+
return false;
80+
}
81+
}
82+
return true;
83+
}
84+
}
85+
}
86+
87+
bool ScopedAddressValue::computeLiveness(PrunedLiveness &liveness) const {
88+
auto addressKind = findTransitiveUsesForAddress(value);
89+
if (addressKind != AddressUseKind::NonEscaping) {
90+
return false;
91+
}
92+
93+
liveness.initializeDefBlock(value->getParentBlock());
94+
visitScopeEndingUses([&](Operand *endOp) {
95+
liveness.updateForUse(endOp->getUser(), /* isLifetimeEnding */ true);
96+
return true;
97+
});
98+
return true;
99+
}
100+
101+
void ScopedAddressValue::createScopeEnd(SILBasicBlock::iterator insertPt,
102+
SILLocation loc) const {
103+
switch (kind) {
104+
case ScopedAddressValueKind::StoreBorrow: {
105+
SILBuilderWithScope(insertPt).createEndBorrow(loc, value);
106+
return;
107+
}
108+
case ScopedAddressValueKind::BeginAccess: {
109+
SILBuilderWithScope(insertPt).createEndAccess(loc, value, false);
110+
return;
111+
}
112+
case ScopedAddressValueKind::Invalid:
113+
llvm_unreachable("Using invalid case?!");
114+
}
115+
}
116+
117+
void ScopedAddressValue::endScopeAtLivenessBoundary(
118+
PrunedLiveness *liveness) const {
119+
// If no users exist, create scope ending instruction immediately after the
120+
// scoped address value.
121+
if (liveness->empty()) {
122+
createScopeEnd(value->getNextInstruction()->getIterator(),
123+
RegularLocation::getAutoGeneratedLocation());
124+
return;
125+
}
126+
127+
PrunedLivenessBoundary scopedAddressBoundary;
128+
scopedAddressBoundary.compute(*liveness);
129+
// Go over the boundary and create scope ending instructions.
130+
scopedAddressBoundary.visitInsertionPoints(
131+
[&](SILBasicBlock::iterator insertPt) {
132+
createScopeEnd(insertPt, RegularLocation::getAutoGeneratedLocation());
133+
});
134+
}
135+
136+
bool swift::hasOtherStoreBorrowsInLifetime(StoreBorrowInst *storeBorrow,
137+
PrunedLiveness *liveness,
138+
DeadEndBlocks *deadEndBlocks) {
139+
SmallVector<StoreBorrowInst *, 4> otherStoreBorrows;
140+
// Collect all other store_borrows to the destination of \p storeBorrow
141+
for (auto *destUse : storeBorrow->getDest()->getUses()) {
142+
if (auto *user = dyn_cast<StoreBorrowInst>(destUse->getUser())) {
143+
if (user == storeBorrow) {
144+
continue;
145+
}
146+
otherStoreBorrows.push_back(user);
147+
}
148+
}
149+
150+
for (auto *otherStoreBorrow : otherStoreBorrows) {
151+
// Return true, if otherStoreBorrow was in \p storeBorrow's scope
152+
if (liveness->isWithinBoundaryOfDef(otherStoreBorrow, storeBorrow)) {
153+
return true;
154+
}
155+
}
156+
return false;
157+
}
158+
159+
void ScopedAddressValue::print(llvm::raw_ostream &os) const {
160+
os << "ScopedAddressIntroducingValue:\n"
161+
"Kind: "
162+
<< kind
163+
<< "\n"
164+
"Value: "
165+
<< value;
166+
}
167+
168+
llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os,
169+
const ScopedAddressValue &value) {
170+
value.print(os);
171+
return os;
172+
}

lib/SIL/Verifier/MemoryLifetimeVerifier.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -274,11 +274,11 @@ void MemoryLifetimeVerifier::requireBitsSet(const Bits &bits, SILValue addr,
274274
}
275275
}
276276

277-
void MemoryLifetimeVerifier::requireNoStoreBorrowLocation(SILValue addr,
278-
SILInstruction *where) {
279-
if (isStoreBorrowLocation(addr)) {
277+
void MemoryLifetimeVerifier::requireNoStoreBorrowLocation(
278+
SILValue addr, SILInstruction *where) {
279+
if (isa<StoreBorrowInst>(addr)) {
280280
reportError("store-borrow location cannot be written",
281-
locations.getLocation(addr)->selfAndParents.find_first(), where);
281+
locations.getLocationIdx(addr), where);
282282
}
283283
}
284284

lib/SIL/Verifier/SILOwnershipVerifier.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "swift/SIL/SILModule.h"
3939
#include "swift/SIL/SILVTable.h"
4040
#include "swift/SIL/SILVisitor.h"
41+
#include "swift/SIL/ScopedAddressUtils.h"
4142
#include "swift/SIL/TypeLowering.h"
4243

4344
#include "llvm/ADT/DenseSet.h"
@@ -363,6 +364,14 @@ bool SILValueOwnershipChecker::gatherUsers(
363364
reborrowVerifier.verifyReborrows(scopedOperand, value);
364365
}
365366

367+
if (auto *svi = dyn_cast<SingleValueInstruction>(op->getUser())) {
368+
if (auto scopedAddress = ScopedAddressValue(svi)) {
369+
scopedAddress.visitScopeEndingUses([&](Operand *endOp) {
370+
nonLifetimeEndingUsers.push_back(endOp);
371+
return true;
372+
});
373+
}
374+
}
366375
// Next see if our use is an interior pointer operand. If we have an
367376
// interior pointer, we need to add all of its address uses as "implicit
368377
// regular users" of our consumed value.

0 commit comments

Comments
 (0)