Skip to content

Commit 9dd3ce5

Browse files
committed
Add a new utility findTransitiveReborrowBaseValuePairs
This visits unique pairs of reborrowPhi and its base value. The code for the utility is mostly refactored from the ReborrowVerifier. This utility will be used later in DCE.
1 parent df82a1a commit 9dd3ce5

File tree

5 files changed

+84
-79
lines changed

5 files changed

+84
-79
lines changed

include/swift/SIL/OwnershipUtils.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,17 @@ bool getAllOwnedValueIntroducers(SILValue value,
998998

999999
OwnedValueIntroducer getSingleOwnedValueIntroducer(SILValue value);
10001000

1001+
using BaseValueSet = SmallPtrSet<SILValue, 8>;
1002+
1003+
/// Starting from \p initialScopeOperand, find all reborrows and their
1004+
/// corresponding base values, and run the visitor function \p
1005+
/// visitReborrowBaseValuePair on them.
1006+
/// Note that a reborrow phi, can have different base values based on different
1007+
/// control flow paths.
1008+
void findTransitiveReborrowBaseValuePairs(
1009+
BorrowingOperand initialScopeOperand, SILValue origBaseValue,
1010+
function_ref<void(SILPhiArgument *, SILValue)> visitReborrowBaseValuePair);
1011+
10011012
} // namespace swift
10021013

10031014
#endif

lib/SIL/Utils/OwnershipUtils.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,3 +1245,55 @@ bool ForwardingOperand::visitForwardedValues(
12451245
return visitor(args[0]);
12461246
});
12471247
}
1248+
1249+
void swift::findTransitiveReborrowBaseValuePairs(
1250+
BorrowingOperand initialScopedOperand, SILValue origBaseValue,
1251+
function_ref<void(SILPhiArgument *, SILValue)> visitReborrowBaseValuePair) {
1252+
// We need a SetVector to make sure we don't revisit the same reborrow operand
1253+
// again.
1254+
SmallSetVector<std::tuple<Operand *, SILValue>, 4> worklist;
1255+
1256+
// Populate the worklist with reborrow and the base value
1257+
initialScopedOperand.visitScopeEndingUses([&](Operand *op) {
1258+
if (op->getOperandOwnership() == OperandOwnership::Reborrow) {
1259+
worklist.insert(std::make_tuple(op, origBaseValue));
1260+
}
1261+
return true;
1262+
});
1263+
1264+
// Size of worklist changes in this loop
1265+
for (unsigned idx = 0; idx < worklist.size(); idx++) {
1266+
Operand *reborrowOp;
1267+
SILValue baseValue;
1268+
std::tie(reborrowOp, baseValue) = worklist[idx];
1269+
1270+
BorrowingOperand borrowingOperand(reborrowOp);
1271+
assert(borrowingOperand.isReborrow());
1272+
1273+
auto *branchInst = cast<BranchInst>(reborrowOp->getUser());
1274+
auto *succBlock = branchInst->getDestBB();
1275+
auto *phiArg = cast<SILPhiArgument>(
1276+
succBlock->getArgument(reborrowOp->getOperandNumber()));
1277+
1278+
SILValue newBaseVal = baseValue;
1279+
// If the previous base value was also passed as a phi arg, that will be
1280+
// the new base value.
1281+
for (auto *arg : succBlock->getArguments()) {
1282+
if (arg->getIncomingPhiValue(branchInst->getParent()) == baseValue) {
1283+
newBaseVal = arg;
1284+
break;
1285+
}
1286+
}
1287+
1288+
// Call the visitor function
1289+
visitReborrowBaseValuePair(phiArg, newBaseVal);
1290+
1291+
BorrowedValue scopedValue(phiArg);
1292+
scopedValue.visitLocalScopeEndingUses([&](Operand *op) {
1293+
if (op->getOperandOwnership() == OperandOwnership::Reborrow) {
1294+
worklist.insert(std::make_tuple(op, newBaseVal));
1295+
}
1296+
return true;
1297+
});
1298+
}
1299+
}

lib/SIL/Verifier/ReborrowVerifier.cpp

Lines changed: 13 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -39,57 +39,18 @@ bool ReborrowVerifier::verifyReborrowLifetime(SILPhiArgument *phiArg,
3939

4040
void ReborrowVerifier::verifyReborrows(BorrowingOperand initialScopedOperand,
4141
SILValue value) {
42-
SmallVector<std::tuple<Operand *, SILValue>, 4> worklist;
43-
// Initialize the worklist with borrow lifetime ending uses
44-
initialScopedOperand.visitScopeEndingUses([&](Operand *op) {
45-
worklist.emplace_back(op, value);
46-
return true;
47-
});
48-
49-
while (!worklist.empty()) {
50-
Operand *borrowLifetimeEndOp;
51-
SILValue baseVal;
52-
std::tie(borrowLifetimeEndOp, baseVal) = worklist.pop_back_val();
53-
auto *borrowLifetimeEndUser = borrowLifetimeEndOp->getUser();
54-
55-
auto borrowingOperand = BorrowingOperand::get(borrowLifetimeEndOp);
56-
if (!borrowingOperand || !borrowingOperand.isReborrow())
57-
continue;
58-
59-
if (isVisitedOp(borrowLifetimeEndOp, baseVal))
60-
continue;
61-
62-
// Process reborrow
63-
auto *branchInst = cast<BranchInst>(borrowLifetimeEndUser);
64-
for (auto *succBlock : branchInst->getSuccessorBlocks()) {
65-
auto *phiArg = cast<SILPhiArgument>(
66-
succBlock->getArgument(borrowLifetimeEndOp->getOperandNumber()));
67-
assert(phiArg->getOwnershipKind() == OwnershipKind::Guaranteed);
68-
69-
SILValue newBaseVal = baseVal;
70-
// If the previous base value was also passed as a phi arg, that will be
71-
// the new base value.
72-
for (auto *arg : succBlock->getArguments()) {
73-
if (arg->getIncomingPhiValue(branchInst->getParent()) == baseVal) {
74-
newBaseVal = arg;
75-
break;
76-
}
77-
}
78-
79-
if (isVisitedPhiArg(phiArg, newBaseVal))
80-
continue;
81-
addVisitedPhiArg(phiArg, newBaseVal);
82-
verifyReborrowLifetime(phiArg, newBaseVal);
83-
84-
// Find the scope ending uses of the guaranteed phi arg and add it to the
85-
// worklist.
86-
auto scopedValue = BorrowedValue(phiArg);
87-
assert(scopedValue);
88-
scopedValue.visitLocalScopeEndingUses([&](Operand *op) {
89-
addVisitedOp(op, newBaseVal);
90-
worklist.emplace_back(op, newBaseVal);
91-
return true;
92-
});
42+
auto visitReborrowBaseValuePair = [&](SILPhiArgument *phiArg,
43+
SILValue baseValue) {
44+
// If the (phiArg, baseValue) pair was not visited before, verify the
45+
// lifetime.
46+
auto it = reborrowToBaseValuesMap.find(phiArg);
47+
if (it == reborrowToBaseValuesMap.end() ||
48+
it->second.find(baseValue) == it->second.end()) {
49+
reborrowToBaseValuesMap[phiArg].insert(baseValue);
50+
verifyReborrowLifetime(phiArg, baseValue);
9351
}
94-
}
52+
};
53+
// For every unique reborrow/base value pair, verify the lifetime
54+
findTransitiveReborrowBaseValuePairs(initialScopedOperand, value,
55+
visitReborrowBaseValuePair);
9556
}

lib/SIL/Verifier/ReborrowVerifierPrivate.h

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,45 +31,27 @@ class ReborrowVerifier {
3131
/// A cache of dead-end basic blocks that we use to determine if we can
3232
/// ignore "leaks".
3333
DeadEndBlocks &deadEndBlocks;
34-
/// A cache map of borrow lifetime ending operands and their base value
35-
llvm::SmallDenseMap<Operand *, SILValue> visitedOps;
36-
/// A cache map of guaranteed phi args and their base value
37-
llvm::SmallDenseMap<SILPhiArgument *, SILValue> visitedPhiArgs;
3834
/// The builder that the checker uses to emit error messages, crash if asked
3935
/// for, or supply back interesting info to the caller.
4036
LinearLifetimeChecker::ErrorBuilder errorBuilder;
37+
/// A map of reborrow phi arg to its base values.
38+
/// Note that a reborrow phi arg can have different base values based on
39+
/// different control flow paths.
40+
llvm::DenseMap<SILPhiArgument *, SmallPtrSet<SILValue, 8>>
41+
reborrowToBaseValuesMap;
4142

4243
public:
4344
ReborrowVerifier(const SILFunction *func, DeadEndBlocks &deadEndBlocks,
4445
LinearLifetimeChecker::ErrorBuilder errorBuilder)
4546
: deadEndBlocks(deadEndBlocks), errorBuilder(errorBuilder) {}
4647

48+
/// Find all the reborrows of \p initialScopedOperand and verify their
49+
/// lifetime.
4750
void verifyReborrows(BorrowingOperand initialScopedOperand, SILValue value);
4851

4952
private:
5053
/// Verifies whether the reborrow's lifetime lies within its base value
5154
bool verifyReborrowLifetime(SILPhiArgument *phiArg, SILValue baseVal);
52-
53-
/// Check if the operand is visited
54-
bool isVisitedOp(Operand *op, SILValue baseVal) {
55-
return visitedOps.find(op) != visitedOps.end();
56-
}
57-
58-
/// Check if the phi arg and base value are visited
59-
bool isVisitedPhiArg(SILPhiArgument *phiArg, SILValue baseVal) {
60-
auto itPhiArg = visitedPhiArgs.find(phiArg);
61-
return itPhiArg != visitedPhiArgs.end() && (*itPhiArg).second == baseVal;
62-
}
63-
64-
/// Mark operand as visited
65-
void addVisitedOp(Operand *op, SILValue baseVal) {
66-
visitedOps.insert({op, baseVal});
67-
}
68-
69-
/// Mark guaranteed phi arg as visited
70-
void addVisitedPhiArg(SILPhiArgument *phiArg, SILValue baseVal) {
71-
visitedPhiArgs.insert({phiArg, baseVal});
72-
}
7355
};
7456

7557
} // namespace swift

test/SIL/ownership-verifier/arguments.sil

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -687,8 +687,7 @@ bb3:
687687
// CHECK: Non Consuming User: br bb2(%5 : $Builtin.NativeObject, %8 : $Builtin.NativeObject)
688688
// CHECK: Block: bb4
689689
// CHECK-LABEL: Error#: 1. End Error in Function: 'simple_loop_carry_over_consume'
690-
//
691-
// CHECK-NOT: Error#: {{[0-9][0-9]*}}. End Error in Function: 'simple_loop_carry_over_consume'
690+
// Some more errors are emitted
692691
sil [ossa] @simple_loop_carry_over_consume : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () {
693692
bb0(%0 : @guaranteed $Builtin.NativeObject):
694693
%1 = begin_borrow %0 : $Builtin.NativeObject

0 commit comments

Comments
 (0)