Skip to content

Commit 3ef6fba

Browse files
authored
Merge pull request #34844 from gottesmm/pr-b83c2f04638cb94494351a5eb9221c6a343adc57
[semantic-arc] Optimize more lifetime joining when a copy_value, destroy_value are in the same block.
2 parents c24e529 + 85965ed commit 3ef6fba

15 files changed

+3224
-1833
lines changed

include/swift/SIL/OwnershipUtils.h

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,34 @@ class ForwardingOperand {
7575
public:
7676
static Optional<ForwardingOperand> get(Operand *use);
7777

78+
Operand *getUse() const { return use; }
7879
ValueOwnershipKind getOwnershipKind() const;
7980
void setOwnershipKind(ValueOwnershipKind newKind) const;
8081
void replaceOwnershipKind(ValueOwnershipKind oldKind,
8182
ValueOwnershipKind newKind) const;
8283

83-
OwnershipForwardingInst *getUser() const {
84+
const OwnershipForwardingInst *operator->() const {
8485
return cast<OwnershipForwardingInst>(use->getUser());
8586
}
87+
OwnershipForwardingInst *operator->() {
88+
return cast<OwnershipForwardingInst>(use->getUser());
89+
}
90+
const OwnershipForwardingInst &operator*() const {
91+
return *cast<OwnershipForwardingInst>(use->getUser());
92+
}
93+
OwnershipForwardingInst &operator*() {
94+
return *cast<OwnershipForwardingInst>(use->getUser());
95+
}
96+
97+
/// Call \p visitor with each value that contains the final forwarded
98+
/// ownership of. E.x.: result of a unchecked_ref_cast, phi arguments of a
99+
/// switch_enum.
100+
bool visitForwardedValues(function_ref<bool(SILValue)> visitor);
101+
102+
/// If statically this forwarded operand has a single forwarded value that the
103+
/// operand forwards ownership into, return that value. Return false
104+
/// otherwise.
105+
SILValue getSingleForwardedValue() const;
86106
};
87107

88108
/// Returns true if the instruction is a 'reborrow'.
@@ -179,7 +199,10 @@ struct BorrowingOperand {
179199
/// Example: An apply performs an instantaneous recursive borrow of a
180200
/// guaranteed value but a begin_apply borrows the value over the entire
181201
/// region of code corresponding to the coroutine.
182-
void visitLocalEndScopeInstructions(function_ref<void(Operand *)> func) const;
202+
///
203+
/// NOTE: Return false from func to stop iterating. Returns false if the
204+
/// closure requested to stop early.
205+
bool visitLocalEndScopeUses(function_ref<bool(Operand *)> func) const;
183206

184207
/// Returns true if this borrow scope operand consumes guaranteed
185208
/// values and produces a new scope afterwards.

lib/SIL/Utils/OwnershipUtils.cpp

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -161,31 +161,33 @@ llvm::raw_ostream &swift::operator<<(llvm::raw_ostream &os,
161161
return os;
162162
}
163163

164-
void BorrowingOperand::visitLocalEndScopeInstructions(
165-
function_ref<void(Operand *)> func) const {
164+
bool BorrowingOperand::visitLocalEndScopeUses(
165+
function_ref<bool(Operand *)> func) const {
166166
switch (kind) {
167167
case BorrowingOperandKind::BeginBorrow:
168168
for (auto *use : cast<BeginBorrowInst>(op->getUser())->getUses()) {
169169
if (use->isLifetimeEnding()) {
170-
func(use);
170+
if (!func(use))
171+
return false;
171172
}
172173
}
173-
return;
174+
return true;
174175
case BorrowingOperandKind::BeginApply: {
175176
auto *user = cast<BeginApplyInst>(op->getUser());
176177
for (auto *use : user->getTokenResult()->getUses()) {
177-
func(use);
178+
if (!func(use))
179+
return false;
178180
}
179-
return;
181+
return true;
180182
}
181183
// These are instantaneous borrow scopes so there aren't any special end
182184
// scope instructions.
183185
case BorrowingOperandKind::Apply:
184186
case BorrowingOperandKind::TryApply:
185187
case BorrowingOperandKind::Yield:
186-
return;
188+
return true;
187189
case BorrowingOperandKind::Branch:
188-
return;
190+
return true;
189191
}
190192
}
191193

@@ -267,7 +269,10 @@ void BorrowingOperand::visitUserResultConsumingUses(
267269
void BorrowingOperand::getImplicitUses(
268270
SmallVectorImpl<Operand *> &foundUses,
269271
std::function<void(Operand *)> *errorFunction) const {
270-
visitLocalEndScopeInstructions([&](Operand *op) { foundUses.push_back(op); });
272+
visitLocalEndScopeUses([&](Operand *op) {
273+
foundUses.push_back(op);
274+
return true;
275+
});
271276
}
272277

273278
//===----------------------------------------------------------------------===//
@@ -842,7 +847,7 @@ Optional<ForwardingOperand> ForwardingOperand::get(Operand *use) {
842847
}
843848

844849
ValueOwnershipKind ForwardingOperand::getOwnershipKind() const {
845-
return getUser()->getOwnershipKind();
850+
return (*this)->getOwnershipKind();
846851
}
847852

848853
void ForwardingOperand::setOwnershipKind(ValueOwnershipKind newKind) const {
@@ -968,3 +973,45 @@ void ForwardingOperand::replaceOwnershipKind(ValueOwnershipKind oldKind,
968973
}
969974
llvm_unreachable("Out of sync with ForwardingOperand::get?!");
970975
}
976+
977+
SILValue ForwardingOperand::getSingleForwardedValue() const {
978+
assert(isGuaranteedForwardingUse(use));
979+
if (auto *svi = dyn_cast<SingleValueInstruction>(use->getUser()))
980+
return svi;
981+
return SILValue();
982+
}
983+
984+
bool ForwardingOperand::visitForwardedValues(
985+
function_ref<bool(SILValue)> visitor) {
986+
auto *user = use->getUser();
987+
988+
assert(isGuaranteedForwardingUse(use));
989+
990+
// See if we have a single value instruction... if we do that is always the
991+
// transitive result.
992+
if (auto *svi = dyn_cast<SingleValueInstruction>(user)) {
993+
return visitor(svi);
994+
}
995+
996+
if (auto *mvri = dyn_cast<MultipleValueInstruction>(user)) {
997+
return llvm::all_of(mvri->getResults(), [&](SILValue value) {
998+
if (value.getOwnershipKind() == OwnershipKind::None)
999+
return true;
1000+
return visitor(value);
1001+
});
1002+
}
1003+
1004+
// This is an instruction like switch_enum and checked_cast_br that are
1005+
// "transforming terminators"... We know that this means that we should at
1006+
// most have a single phi argument.
1007+
auto *ti = cast<TermInst>(user);
1008+
return llvm::all_of(ti->getSuccessorBlocks(), [&](SILBasicBlock *succBlock) {
1009+
// If we do not have any arguments, then continue.
1010+
if (succBlock->args_empty())
1011+
return true;
1012+
1013+
auto args = succBlock->getSILPhiArguments();
1014+
assert(args.size() == 1 && "Transforming terminator with multiple args?!");
1015+
return visitor(args[0]);
1016+
});
1017+
}

lib/SIL/Verifier/ReborrowVerifier.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ void ReborrowVerifier::verifyReborrows(BorrowingOperand initialScopedOperand,
4242
SILValue value) {
4343
SmallVector<std::tuple<Operand *, SILValue>, 4> worklist;
4444
// Initialize the worklist with borrow lifetime ending uses
45-
initialScopedOperand.visitLocalEndScopeInstructions([&](Operand *op) {
45+
initialScopedOperand.visitLocalEndScopeUses([&](Operand *op) {
4646
worklist.emplace_back(op, value);
47+
return true;
4748
});
4849

4950
while (!worklist.empty()) {

lib/SILOptimizer/SemanticARC/BorrowScopeOpts.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,17 @@
1818
///
1919
//===----------------------------------------------------------------------===//
2020

21+
#include "Context.h"
2122
#include "SemanticARCOptVisitor.h"
2223

2324
using namespace swift;
2425
using namespace swift::semanticarc;
2526

2627
bool SemanticARCOptVisitor::visitBeginBorrowInst(BeginBorrowInst *bbi) {
28+
// Quickly check if we are supposed to perform this transformation.
29+
if (!ctx.shouldPerform(ARCTransformKind::RedundantBorrowScopeElimPeephole))
30+
return false;
31+
2732
auto kind = bbi->getOperand().getOwnershipKind();
2833
SmallVector<EndBorrowInst *, 16> endBorrows;
2934
for (auto *op : bbi->getUses()) {

lib/SILOptimizer/SemanticARC/Context.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define SWIFT_SILOPTIMIZER_SEMANTICARC_CONTEXT_H
1515

1616
#include "OwnershipLiveRange.h"
17+
#include "SemanticARCOpts.h"
1718

1819
#include "swift/Basic/BlotSetVector.h"
1920
#include "swift/Basic/FrozenMultiMap.h"
@@ -30,6 +31,7 @@ namespace semanticarc {
3031

3132
struct LLVM_LIBRARY_VISIBILITY Context {
3233
SILFunction &fn;
34+
ARCTransformKind transformKind = ARCTransformKind::All;
3335
Optional<DeadEndBlocks> deadEndBlocks;
3436
ValueLifetimeAnalysis::Frontier lifetimeFrontier;
3537
SmallMultiMapCache<SILValue, Operand *> addressToExhaustiveWriteListCache;
@@ -91,6 +93,25 @@ struct LLVM_LIBRARY_VISIBILITY Context {
9193

9294
void verify() const;
9395

96+
bool shouldPerform(ARCTransformKind testKind) const {
97+
// When asserts are enabled, we allow for specific arc transforms to be
98+
// turned on/off via LLVM args. So check that if we have asserts, perform
99+
// all optimizations otherwise.
100+
#ifndef NDEBUG
101+
if (transformKind == ARCTransformKind::Invalid)
102+
return false;
103+
return bool(testKind & transformKind);
104+
#else
105+
return true;
106+
#endif
107+
}
108+
109+
void reset() {
110+
lifetimeFrontier.clear();
111+
addressToExhaustiveWriteListCache.clear();
112+
joinedOwnedIntroducerToConsumedOperands.reset();
113+
}
114+
94115
private:
95116
static bool
96117
constructCacheValue(SILValue initialValue,

0 commit comments

Comments
 (0)