Skip to content

Commit 6c46841

Browse files
authored
Merge pull request #34438 from meg-gupta/reborrowverifier
[ownership] Add a new ReborrowVerifier
2 parents b0719f0 + 601ea65 commit 6c46841

13 files changed

+1656
-295
lines changed

include/swift/SIL/LinearLifetimeChecker.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class LinearLifetimeChecker {
5050
class ErrorBuilder;
5151

5252
private:
53+
friend class ReborrowVerifier;
5354
friend class SILOwnershipVerifier;
5455
friend class SILValueOwnershipChecker;
5556

include/swift/SIL/OwnershipUtils.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ bool isOwnedForwardingInstruction(SILInstruction *inst);
6666
/// previous terminator.
6767
bool isOwnedForwardingValue(SILValue value);
6868

69+
/// Returns true if the instruction is a 'reborrow'.
70+
bool isReborrowInstruction(const SILInstruction *inst);
71+
6972
class BorrowingOperandKind {
7073
public:
7174
enum Kind : uint8_t {
@@ -134,7 +137,7 @@ struct BorrowingOperand {
134137
return BorrowingOperand(*kind, op);
135138
}
136139

137-
void visitEndScopeInstructions(function_ref<void(Operand *)> func) const;
140+
void visitLocalEndScopeInstructions(function_ref<void(Operand *)> func) const;
138141

139142
/// Returns true if this borrow scope operand consumes guaranteed
140143
/// values and produces a new scope afterwards.
@@ -204,7 +207,7 @@ struct BorrowingOperand {
204207
/// \p errorFunction a callback that if non-null is passed an operand that
205208
/// triggers a mal-formed SIL error. This is just needed for the ownership
206209
/// verifier to emit good output.
207-
bool getImplicitUses(
210+
void getImplicitUses(
208211
SmallVectorImpl<Operand *> &foundUses,
209212
std::function<void(Operand *)> *errorFunction = nullptr) const;
210213

include/swift/SIL/SILValue.h

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ class SILInstruction;
4040
class SILLocation;
4141
class DeadEndBlocks;
4242
class ValueBaseUseIterator;
43-
class ValueUseIterator;
43+
class ConsumingUseIterator;
44+
class NonConsumingUseIterator;
4445
class SILValue;
4546

4647
/// An enumeration which contains values for all the concrete ValueBase
@@ -263,10 +264,20 @@ class ValueBase : public SILNode, public SILAllocated<ValueBase> {
263264

264265
using use_iterator = ValueBaseUseIterator;
265266
using use_range = iterator_range<use_iterator>;
267+
using consuming_use_iterator = ConsumingUseIterator;
268+
using consuming_use_range = iterator_range<consuming_use_iterator>;
269+
using non_consuming_use_iterator = NonConsumingUseIterator;
270+
using non_consuming_use_range = iterator_range<non_consuming_use_iterator>;
266271

267272
inline use_iterator use_begin() const;
268273
inline use_iterator use_end() const;
269274

275+
inline consuming_use_iterator consuming_use_begin() const;
276+
inline consuming_use_iterator consuming_use_end() const;
277+
278+
inline non_consuming_use_iterator non_consuming_use_begin() const;
279+
inline non_consuming_use_iterator non_consuming_use_end() const;
280+
270281
/// Returns a range of all uses, which is useful for iterating over all uses.
271282
/// To ignore debug-info instructions use swift::getNonDebugUses instead
272283
/// (see comment in DebugUtils.h).
@@ -285,6 +296,12 @@ class ValueBase : public SILNode, public SILAllocated<ValueBase> {
285296
/// and it has a single consuming user. Returns .none otherwise.
286297
inline Operand *getSingleConsumingUse() const;
287298

299+
/// Returns a range of all consuming uses
300+
inline consuming_use_range getConsumingUses() const;
301+
302+
/// Returns a range of all non consuming uses
303+
inline non_consuming_use_range getNonConsumingUses() const;
304+
288305
template <class T>
289306
inline T *getSingleUserOfType() const;
290307

@@ -711,8 +728,10 @@ class Operand {
711728
TheValue->FirstUse = this;
712729
}
713730

731+
friend class ValueBase;
714732
friend class ValueBaseUseIterator;
715-
friend class ValueUseIterator;
733+
friend class ConsumingUseIterator;
734+
friend class NonConsumingUseIterator;
716735
template <unsigned N> friend class FixedOperandList;
717736
friend class TrailingOperandsList;
718737
};
@@ -729,6 +748,7 @@ using OperandValueArrayRef = ArrayRefView<Operand, SILValue, getSILValueType>;
729748
/// An iterator over all uses of a ValueBase.
730749
class ValueBaseUseIterator : public std::iterator<std::forward_iterator_tag,
731750
Operand*, ptrdiff_t> {
751+
protected:
732752
Operand *Cur;
733753
public:
734754
ValueBaseUseIterator() = default;
@@ -770,6 +790,74 @@ inline ValueBase::use_iterator ValueBase::use_end() const {
770790
inline iterator_range<ValueBase::use_iterator> ValueBase::getUses() const {
771791
return { use_begin(), use_end() };
772792
}
793+
794+
class ConsumingUseIterator : public ValueBaseUseIterator {
795+
public:
796+
explicit ConsumingUseIterator(Operand *cur) : ValueBaseUseIterator(cur) {}
797+
ConsumingUseIterator &operator++() {
798+
assert(Cur && "incrementing past end()!");
799+
assert(Cur->isConsumingUse());
800+
while ((Cur = Cur->NextUse)) {
801+
if (Cur->isConsumingUse())
802+
break;
803+
}
804+
return *this;
805+
}
806+
807+
ConsumingUseIterator operator++(int unused) {
808+
ConsumingUseIterator copy = *this;
809+
++*this;
810+
return copy;
811+
}
812+
};
813+
814+
inline ValueBase::consuming_use_iterator
815+
ValueBase::consuming_use_begin() const {
816+
auto cur = FirstUse;
817+
while (cur && !cur->isConsumingUse()) {
818+
cur = cur->NextUse;
819+
}
820+
return ValueBase::consuming_use_iterator(cur);
821+
}
822+
823+
inline ValueBase::consuming_use_iterator ValueBase::consuming_use_end() const {
824+
return ValueBase::consuming_use_iterator(nullptr);
825+
}
826+
827+
class NonConsumingUseIterator : public ValueBaseUseIterator {
828+
public:
829+
explicit NonConsumingUseIterator(Operand *cur) : ValueBaseUseIterator(cur) {}
830+
NonConsumingUseIterator &operator++() {
831+
assert(Cur && "incrementing past end()!");
832+
assert(!Cur->isConsumingUse());
833+
while ((Cur = Cur->NextUse)) {
834+
if (!Cur->isConsumingUse())
835+
break;
836+
}
837+
return *this;
838+
}
839+
840+
NonConsumingUseIterator operator++(int unused) {
841+
NonConsumingUseIterator copy = *this;
842+
++*this;
843+
return copy;
844+
}
845+
};
846+
847+
inline ValueBase::non_consuming_use_iterator
848+
ValueBase::non_consuming_use_begin() const {
849+
auto cur = FirstUse;
850+
while (cur && cur->isConsumingUse()) {
851+
cur = cur->NextUse;
852+
}
853+
return ValueBase::non_consuming_use_iterator(cur);
854+
}
855+
856+
inline ValueBase::non_consuming_use_iterator
857+
ValueBase::non_consuming_use_end() const {
858+
return ValueBase::non_consuming_use_iterator(nullptr);
859+
}
860+
773861
inline bool ValueBase::hasOneUse() const {
774862
auto I = use_begin(), E = use_end();
775863
if (I == E) return false;
@@ -806,6 +894,15 @@ inline Operand *ValueBase::getSingleConsumingUse() const {
806894
return result;
807895
}
808896

897+
inline ValueBase::consuming_use_range ValueBase::getConsumingUses() const {
898+
return {consuming_use_begin(), consuming_use_end()};
899+
}
900+
901+
inline ValueBase::non_consuming_use_range
902+
ValueBase::getNonConsumingUses() const {
903+
return {non_consuming_use_begin(), non_consuming_use_end()};
904+
}
905+
809906
inline bool ValueBase::hasTwoUses() const {
810907
auto iter = use_begin(), end = use_end();
811908
for (unsigned i = 0; i < 2; ++i) {

lib/SIL/Utils/OwnershipUtils.cpp

Lines changed: 12 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,15 @@ bool swift::isOwnershipForwardingInst(SILInstruction *i) {
125125
return isOwnershipForwardingValueKind(SILNodeKind(i->getKind()));
126126
}
127127

128+
bool swift::isReborrowInstruction(const SILInstruction *i) {
129+
switch (i->getKind()) {
130+
case SILInstructionKind::BranchInst:
131+
return true;
132+
default:
133+
return false;
134+
}
135+
}
136+
128137
//===----------------------------------------------------------------------===//
129138
// Borrowing Operand
130139
//===----------------------------------------------------------------------===//
@@ -163,7 +172,7 @@ llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os,
163172
return os;
164173
}
165174

166-
void BorrowingOperand::visitEndScopeInstructions(
175+
void BorrowingOperand::visitLocalEndScopeInstructions(
167176
function_ref<void(Operand *)> func) const {
168177
switch (kind) {
169178
case BorrowingOperandKind::BeginBorrow:
@@ -181,18 +190,8 @@ void BorrowingOperand::visitEndScopeInstructions(
181190
return;
182191
}
183192
case BorrowingOperandKind::Branch:
184-
for (auto *succBlock :
185-
cast<BranchInst>(op->getUser())->getSuccessorBlocks()) {
186-
auto *arg = succBlock->getArgument(op->getOperandNumber());
187-
for (auto *use : arg->getUses()) {
188-
if (use->isConsumingUse()) {
189-
func(use);
190-
}
191-
}
192-
}
193193
return;
194194
}
195-
llvm_unreachable("Covered switch isn't covered");
196195
}
197196

198197
void BorrowingOperand::visitBorrowIntroducingUserResults(
@@ -267,46 +266,10 @@ void BorrowingOperand::visitUserResultConsumingUses(
267266
}
268267
}
269268

270-
bool BorrowingOperand::getImplicitUses(
269+
void BorrowingOperand::getImplicitUses(
271270
SmallVectorImpl<Operand *> &foundUses,
272271
std::function<void(Operand *)> *errorFunction) const {
273-
if (!areAnyUserResultsBorrowIntroducers()) {
274-
visitEndScopeInstructions([&](Operand *op) { foundUses.push_back(op); });
275-
return false;
276-
}
277-
278-
// Ok, we have an instruction that introduces a new borrow scope and its
279-
// result is that borrow scope. In such a case, we need to not just add the
280-
// end scope instructions of this scoped operand, but also look through any
281-
// guaranteed phis and add their end_borrow to this list as well.
282-
SmallVector<BorrowingOperand, 8> worklist;
283-
SmallPtrSet<Operand *, 8> visitedValue;
284-
worklist.push_back(*this);
285-
visitedValue.insert(op);
286-
bool foundError = false;
287-
while (!worklist.empty()) {
288-
auto scopedOperand = worklist.pop_back_val();
289-
scopedOperand.visitConsumingUsesOfBorrowIntroducingUserResults(
290-
[&](Operand *op) {
291-
if (auto subSub = BorrowingOperand::get(op)) {
292-
if (!visitedValue.insert(op).second) {
293-
if (errorFunction) {
294-
(*errorFunction)(op);
295-
}
296-
foundError = true;
297-
return;
298-
}
299-
300-
worklist.push_back(*subSub);
301-
visitedValue.insert(subSub->op);
302-
return;
303-
}
304-
305-
foundUses.push_back(op);
306-
});
307-
}
308-
309-
return foundError;
272+
visitLocalEndScopeInstructions([&](Operand *op) { foundUses.push_back(op); });
310273
}
311274

312275
//===----------------------------------------------------------------------===//

lib/SIL/Verifier/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ target_sources(swiftSIL PRIVATE
22
LoadBorrowImmutabilityChecker.cpp
33
LinearLifetimeChecker.cpp
44
MemoryLifetime.cpp
5+
ReborrowVerifier.cpp
56
SILOwnershipVerifier.cpp
67
SILVerifier.cpp)

lib/SIL/Verifier/LinearLifetimeChecker.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -305,10 +305,14 @@ void State::checkForSameBlockUseAfterFree(Operand *consumingUse,
305305
// must be instructions in the given block. Make sure that the non consuming
306306
// user is strictly before the consuming user.
307307
for (auto *nonConsumingUse : nonConsumingUsesInBlock) {
308-
if (std::find_if(consumingUse->getUser()->getIterator(), userBlock->end(),
309-
[&nonConsumingUse](const SILInstruction &i) -> bool {
310-
return nonConsumingUse->getUser() == &i;
311-
}) == userBlock->end()) {
308+
if (nonConsumingUse->getUser() != consumingUse->getUser()) {
309+
if (std::find_if(consumingUse->getUser()->getIterator(), userBlock->end(),
310+
[&nonConsumingUse](const SILInstruction &i) -> bool {
311+
return nonConsumingUse->getUser() == &i;
312+
}) == userBlock->end()) {
313+
continue;
314+
}
315+
} else if (isReborrowInstruction(consumingUse->getUser())) {
312316
continue;
313317
}
314318

0 commit comments

Comments
 (0)