Skip to content

Commit 465ba69

Browse files
committed
SIL: handle the borrowed-from instruction in the C++ ownership utilities and passes
1 parent 97c368a commit 465ba69

File tree

9 files changed

+88
-22
lines changed

9 files changed

+88
-22
lines changed

include/swift/SIL/OwnershipUtils.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ inline bool isForwardingConsume(SILValue value) {
9090
// Ownership Def-Use Utilities
9191
//===----------------------------------------------------------------------===//
9292

93+
SILValue lookThroughSingleBorrowedFromUser(SILValue v);
94+
SILValue lookThroughBorrowedFromDef(SILValue v);
95+
void getTransitiveConsumingUses(SILValue v, llvm::SmallVectorImpl<Operand *> &results);
96+
9397
/// Whether the specified OSSA-lifetime introducer has a pointer escape.
9498
///
9599
/// precondition: \p value introduces an OSSA-lifetime, either a BorrowedValue

lib/SIL/IR/SILArgument.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "swift/SIL/SILFunction.h"
1717
#include "swift/SIL/SILInstruction.h"
1818
#include "swift/SIL/SILModule.h"
19+
#include "swift/SIL/OwnershipUtils.h"
1920
#include "llvm/ADT/STLExtras.h"
2021

2122
using namespace swift;
@@ -291,7 +292,8 @@ bool SILPhiArgument::visitTransitiveIncomingPhiOperands(
291292
argument->getIncomingPhiOperands(operands);
292293

293294
for (auto *operand : operands) {
294-
SILPhiArgument *forwarded = dyn_cast<SILPhiArgument>(operand->get());
295+
SILValue opVal = lookThroughBorrowedFromDef(operand->get());
296+
SILPhiArgument *forwarded = dyn_cast<SILPhiArgument>(opVal);
295297
if (forwarded && forwarded->isPhi()) {
296298
worklist.insert(forwarded);
297299
}

lib/SIL/Utils/OwnershipUtils.cpp

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ bool swift::computeIsGuaranteedForwarding(SILValue value) {
168168
if (value->getOwnershipKind() != OwnershipKind::Guaranteed) {
169169
return false;
170170
}
171+
value = lookThroughBorrowedFromDef(value);
171172
// NOTE: canOpcodeForwardInnerGuaranteedValues returns true for transformation
172173
// terminator results.
173174
if (canOpcodeForwardInnerGuaranteedValues(value) ||
@@ -185,9 +186,9 @@ bool swift::computeIsGuaranteedForwarding(SILValue value) {
185186
// OwnershipVerifier.
186187
bool isGuaranteedForwardingPhi = false;
187188
phi->visitTransitiveIncomingPhiOperands([&](auto *, auto *op) -> bool {
188-
auto opValue = op->get();
189-
assert(opValue->getOwnershipKind().isCompatibleWith(
189+
assert(op->get()->getOwnershipKind().isCompatibleWith(
190190
OwnershipKind::Guaranteed));
191+
auto opValue = lookThroughBorrowedFromDef(op->get());
191192
if (canOpcodeForwardInnerGuaranteedValues(opValue) ||
192193
isa<SILFunctionArgument>(opValue)) {
193194
isGuaranteedForwardingPhi = true;
@@ -202,6 +203,41 @@ bool swift::computeIsGuaranteedForwarding(SILValue value) {
202203
return isGuaranteedForwardingPhi;
203204
}
204205

206+
SILValue swift::lookThroughSingleBorrowedFromUser(SILValue v) {
207+
BorrowedFromInst *singleBfi = nullptr;
208+
for (auto *use : v->getUses()) {
209+
if (auto *bfi = dyn_cast<BorrowedFromInst>(use->getUser())) {
210+
if (use->getOperandNumber() == 0) {
211+
if (singleBfi)
212+
return v;
213+
singleBfi = bfi;
214+
}
215+
}
216+
}
217+
if (singleBfi)
218+
return singleBfi;
219+
return v;
220+
}
221+
222+
SILValue swift::lookThroughBorrowedFromDef(SILValue v) {
223+
while (auto *bfi = dyn_cast<BorrowedFromInst>(v)) {
224+
v = bfi->getBorrowedValue();
225+
}
226+
return v;
227+
}
228+
229+
void swift::getTransitiveConsumingUses(SILValue v, llvm::SmallVectorImpl<Operand *> &results) {
230+
for (Operand *use : v->getUses()) {
231+
if (use->isLifetimeEnding()) {
232+
results.push_back(use);
233+
} else if (auto *bfi = dyn_cast<BorrowedFromInst>(use->getUser())) {
234+
if (use->getOperandNumber() == 0) {
235+
getTransitiveConsumingUses(bfi, results);
236+
}
237+
}
238+
}
239+
}
240+
205241
//===----------------------------------------------------------------------===//
206242
// Guaranteed Use-Point (Lifetime) Discovery
207243
//===----------------------------------------------------------------------===//
@@ -896,26 +932,28 @@ void BorrowedValue::getLocalScopeEndingInstructions(
896932

897933
// Note: BorrowedLifetimeExtender assumes no intermediate values between a
898934
// borrow introducer and its reborrow. The borrowed value must be an operand of
899-
// the reborrow.
935+
// the reborrow. Exception: it looks through `borrowed-from` of a reborrow phi.
900936
bool BorrowedValue::visitLocalScopeEndingUses(
901937
function_ref<bool(Operand *)> visitor) const {
902938
assert(isLocalScope() && "Should only call this given a local scope");
939+
SILValue v = value;
903940
switch (kind) {
904941
case BorrowedValueKind::Invalid:
905942
llvm_unreachable("Using invalid case?!");
906943
case BorrowedValueKind::SILFunctionArgument:
907944
llvm_unreachable("Should only call this with a local scope");
908-
case BorrowedValueKind::LoadBorrow:
909-
case BorrowedValueKind::BeginBorrow:
910945
case BorrowedValueKind::Phi:
911-
for (auto *use : value->getUses()) {
912-
if (use->isLifetimeEnding()) {
913-
if (!visitor(use))
914-
return false;
915-
}
946+
case BorrowedValueKind::LoadBorrow:
947+
case BorrowedValueKind::BeginBorrow: {
948+
llvm::SmallVector<Operand *, 16> consumingUses;
949+
getTransitiveConsumingUses(v, consumingUses);
950+
for (Operand *use : consumingUses) {
951+
if (!visitor(use))
952+
return false;
916953
}
917954
return true;
918955
}
956+
}
919957
llvm_unreachable("Covered switch isn't covered?!");
920958
}
921959

@@ -1677,7 +1715,7 @@ void swift::visitExtendedGuaranteedForwardingPhiBaseValuePairs(
16771715
// then set newBaseValue to the reborrow.
16781716
for (auto &op : phiOp.getBranch()->getAllOperands()) {
16791717
PhiOperand otherPhiOp(&op);
1680-
if (otherPhiOp.getSource() != currentBaseValue) {
1718+
if (lookThroughBorrowedFromDef(otherPhiOp.getSource()) != currentBaseValue) {
16811719
continue;
16821720
}
16831721
newBaseValue = otherPhiOp.getValue();
@@ -1974,6 +2012,8 @@ class FindEnclosingDefs {
19742012

19752013
assert(incomingValue->getOwnershipKind() == OwnershipKind::Guaranteed);
19762014

2015+
incomingValue = lookThroughBorrowedFromDef(incomingValue);
2016+
19772017
// Avoid repeatedly constructing BorrowedValue during use-def
19782018
// traversal. That would be quadratic if it checks all uses for reborrows.
19792019
if (auto *predPhi = dyn_cast<SILPhiArgument>(incomingValue)) {
@@ -2257,7 +2297,9 @@ void swift::visitTransitiveEndBorrows(
22572297

22582298
while (!worklist.empty()) {
22592299
auto val = worklist.pop();
2260-
for (auto *consumingUse : val->getConsumingUses()) {
2300+
llvm::SmallVector<Operand *, 16> consumingUses;
2301+
getTransitiveConsumingUses(val, consumingUses);
2302+
for (auto *consumingUse : consumingUses) {
22612303
auto *consumingUser = consumingUse->getUser();
22622304
if (auto *branch = dyn_cast<BranchInst>(consumingUser)) {
22632305
auto *succBlock = branch->getSingleSuccessorBlock();

lib/SIL/Verifier/GuaranteedPhiVerifier.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@ bool GuaranteedPhiVerifier::verifyDependentPhiLifetime(SILPhiArgument *phi,
2828
dependentPhiToBaseValueMap[phi].insert(baseValue);
2929

3030
// Now, verify whether phi's lifetime is within baseValue's lifetime
31-
SmallVector<Operand *, 4> baseValConsumingUses(baseValue->getConsumingUses());
31+
SmallVector<Operand *, 4> baseValConsumingUses;
32+
getTransitiveConsumingUses(baseValue, baseValConsumingUses);
3233
// If the baseValue has no consuming uses, there is nothing more to verify
3334
if (baseValConsumingUses.empty())
3435
return false;
3536

36-
SmallVector<Operand *, 4> phiUses(phi->getUses());
37+
SmallVector<Operand *, 4> phiUses;
38+
getTransitiveConsumingUses(phi, phiUses);
3739
LinearLifetimeChecker checker(deadEndBlocks);
3840
// newErrorBuilder is consumed at the end of the checkValue function.
3941
// Copy initial state from errorBuilder everytime

lib/SIL/Verifier/SILOwnershipVerifier.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,7 @@ bool SILValueOwnershipChecker::
639639
bool foundGuaranteedForwardingPhiOperand = false;
640640
bool foundNonGuaranteedForwardingPhiOperand = false;
641641
phi->visitTransitiveIncomingPhiOperands([&](auto *, auto *operand) -> bool {
642-
auto value = operand->get();
642+
auto value = lookThroughBorrowedFromDef(operand->get());
643643
if (canOpcodeForwardInnerGuaranteedValues(value) ||
644644
isa<SILFunctionArgument>(value)) {
645645
foundGuaranteedForwardingPhiOperand = true;

lib/SILOptimizer/SemanticARC/OwnershipLiveRange.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,10 @@ OwnershipLiveRange::OwnershipLiveRange(SILValue value)
105105
// the users force the live range to be alive.
106106
if (!ti) {
107107
for (SILValue v : user->getResults()) {
108-
if (v->getOwnershipKind() != OwnershipKind::Owned)
108+
if (v->getOwnershipKind() != OwnershipKind::Owned &&
109+
!isa<BorrowedFromInst>(user)) {
109110
continue;
111+
}
110112
llvm::copy(v->getUses(), std::back_inserter(worklist));
111113
}
112114
continue;

lib/SILOptimizer/Transforms/ConditionForwarding.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "swift/SIL/SILBuilder.h"
1919
#include "swift/SIL/SILInstruction.h"
2020
#include "swift/SIL/SILUndef.h"
21+
#include "swift/SILOptimizer/Utils/OwnershipOptUtils.h"
2122
#include "swift/SILOptimizer/PassManager/Passes.h"
2223
#include "swift/SILOptimizer/PassManager/Transforms.h"
2324

@@ -158,13 +159,15 @@ static bool hasNoRelevantSideEffects(SILBasicBlock *BB) {
158159
bool ConditionForwarding::tryOptimize(SwitchEnumInst *SEI) {
159160
// The switch_enum argument (an Enum) must be a block argument at the merging
160161
// point of the condition's destinations.
161-
auto *Arg = dyn_cast<SILArgument>(SEI->getOperand());
162+
auto *Arg = dyn_cast<SILArgument>(lookThroughBorrowedFromDef(SEI->getOperand()));
162163
if (!Arg)
163164
return false;
164165

166+
SILValue argValue = lookThroughSingleBorrowedFromUser(Arg);
167+
165168
// The switch_enum must be the only use of the Enum, except it may be used in
166169
// SEI's successors.
167-
for (Operand *ArgUse : Arg->getUses()) {
170+
for (Operand *ArgUse : argValue->getUses()) {
168171
SILInstruction *ArgUser = ArgUse->getUser();
169172
if (ArgUser == SEI)
170173
continue;
@@ -242,8 +245,8 @@ bool ConditionForwarding::tryOptimize(SwitchEnumInst *SEI) {
242245
// First thing to do is to replace all uses of the Enum (= the merging block
243246
// argument), as this argument gets deleted.
244247
BasicBlockSet NeedEnumArg(BB->getParent());
245-
while (!Arg->use_empty()) {
246-
Operand *ArgUse = *Arg->use_begin();
248+
while (!argValue->use_empty()) {
249+
Operand *ArgUse = *argValue->use_begin();
247250
SILInstruction *ArgUser = ArgUse->getUser();
248251
if (ArgUser->isDebugInstruction()) {
249252
// Don't care about debug instructions. Just remove them.
@@ -318,6 +321,9 @@ bool ConditionForwarding::tryOptimize(SwitchEnumInst *SEI) {
318321
}
319322
}
320323
}
324+
if (argValue != Arg) {
325+
cast<BorrowedFromInst>(argValue)->eraseFromParent();
326+
}
321327

322328
// Final step: replace the switch_enum by the condition.
323329
SILBuilder B(Condition);

lib/SILOptimizer/Transforms/DeadCodeElimination.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,10 @@ void DCE::markLive() {
294294
addReverseDependency(I.getOperand(0), &I);
295295
break;
296296
}
297+
case SILInstructionKind::BorrowedFromInst: {
298+
addReverseDependency(I.getOperand(0), &I);
299+
break;
300+
}
297301
case SILInstructionKind::EndLifetimeInst: {
298302
// The instruction is live only if it's operand value is also live
299303
addReverseDependency(I.getOperand(0), &I);
@@ -576,7 +580,7 @@ void DCE::endLifetimeOfLiveValue(SILValue value, SILInstruction *insertPt) {
576580
builder.emitDestroyOperation(RegularLocation::getAutoGeneratedLocation(),
577581
value);
578582
}
579-
BorrowedValue borrow(value);
583+
BorrowedValue borrow(lookThroughBorrowedFromDef(value));
580584
if (borrow && borrow.isLocalScope()) {
581585
builder.emitEndBorrowOperation(RegularLocation::getAutoGeneratedLocation(),
582586
value);

lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ bool CanonicalizeOSSALifetime::computeCanonicalLiveness() {
155155
defUseWorklist.insert(copy);
156156
continue;
157157
}
158+
if (auto *bfi = dyn_cast<BorrowedFromInst>(user)) {
159+
defUseWorklist.insert(bfi);
160+
continue;
161+
}
158162
// Handle debug_value instructions separately.
159163
if (pruneDebugMode) {
160164
if (auto *dvi = dyn_cast<DebugValueInst>(user)) {

0 commit comments

Comments
 (0)