Skip to content

Commit 275c341

Browse files
authored
Merge pull request #39740 from atrick/oou-borrow
OSSA utility incremental redesign and SILCombine fixes
2 parents 22aa3c5 + 16d3be2 commit 275c341

File tree

5 files changed

+454
-525
lines changed

5 files changed

+454
-525
lines changed

include/swift/SILOptimizer/Utils/OwnershipOptUtils.h

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,10 @@ struct OwnershipFixupContext {
148148
InstModCallbacks &callbacks;
149149
DeadEndBlocks &deBlocks;
150150

151-
152-
// FIXME: remove these two vectors once BorrowedLifetimeExtender is used
153-
// everywhere.
154-
SmallVector<Operand *, 8> transitiveBorrowedUses;
155-
SmallVector<PhiOperand, 8> recursiveReborrows;
151+
// Cache the use-points for the lifetime of an inner guaranteed value (which
152+
// does not introduce a borrow scope) after checking validity. These will be
153+
// used again to extend the lifetime of the replacement value.
154+
SmallVector<Operand *, 8> guaranteedUsePoints;
156155

157156
/// Extra state initialized by OwnershipRAUWFixupHelper::get() that we use
158157
/// when RAUWing addresses. This ensures we do not need to recompute this
@@ -162,6 +161,8 @@ struct OwnershipFixupContext {
162161
/// compute all transitive address uses of oldValue. If we find that we do
163162
/// need this fixed up, then we will copy our interior pointer base value
164163
/// and use this to seed that new lifetime.
164+
///
165+
/// FIXME: shouldn't these already be covered by guaranteedUsePoints?
165166
SmallVector<Operand *, 8> allAddressUsesFromOldValue;
166167

167168
/// This is the interior pointer (e.g. ref_element_addr)
@@ -184,8 +185,7 @@ struct OwnershipFixupContext {
184185
: callbacks(callbacks), deBlocks(deBlocks) {}
185186

186187
void clear() {
187-
transitiveBorrowedUses.clear();
188-
recursiveReborrows.clear();
188+
guaranteedUsePoints.clear();
189189
extraAddressFixupInfo.allAddressUsesFromOldValue.clear();
190190
extraAddressFixupInfo.base = AccessBase();
191191
}
@@ -212,6 +212,8 @@ class OwnershipRAUWHelper {
212212
private:
213213
OwnershipFixupContext *ctx;
214214
SILValue oldValue;
215+
// newValue is the aspirational replacement. It might not be the actual
216+
// replacement after SILCombine fixups (like type casting) and OSSA fixups.
215217
SILValue newValue;
216218

217219
public:
@@ -253,21 +255,27 @@ class OwnershipRAUWHelper {
253255
/// Perform OSSA fixup on newValue and return a fixed-up value based that can
254256
/// be used to replace all uses of oldValue.
255257
///
256-
/// This is so that we can avoid creating "forwarding" transformation
257-
/// instructions before we know if we can perform the RAUW. Any such
258-
/// "forwarding" transformation must be performed upon \p newValue at \p
259-
/// oldValue's insertion point so that we can then here RAUW the transformed
260-
/// \p newValue.
258+
/// This is only used by clients that must transform \p newValue, such as
259+
/// adding type casts, before it can be used to replace \p oldValue.
260+
///
261+
/// \p rewrittenNewValue is only passed when the client needs to regenerate
262+
/// newValue after checking its RAUW validity, but before performing OSSA
263+
/// fixup on it.
264+
SILValue prepareReplacement(SILValue rewrittenNewValue = SILValue());
265+
266+
/// Perform the actual RAUW--replace all uses if \p oldValue.
267+
///
268+
/// Precondition: \p replacementValue is either invalid or has the same type
269+
/// as \p oldValue and is a valid OSSA replacement.
261270
SILBasicBlock::iterator
262-
perform(SingleValueInstruction *maybeTransformedNewValue = nullptr);
271+
perform(SILValue replacementValue = SILValue());
263272

264273
private:
265-
SILBasicBlock::iterator replaceAddressUses(SingleValueInstruction *oldValue,
266-
SILValue newValue);
267-
268274
void invalidate() {
269275
ctx = nullptr;
270276
}
277+
278+
SILValue getReplacementAddress();
271279
};
272280

273281
/// A utility composed ontop of OwnershipFixupContext that knows how to replace

lib/SILOptimizer/SILCombiner/SILCombinerCastVisitors.cpp

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -237,13 +237,17 @@ visitPointerToAddressInst(PointerToAddressInst *PTAI) {
237237
// handle issues around interior pointers and expanding borrow scopes.
238238
if (auto *ATPI = dyn_cast<AddressToPointerInst>(PTAI->getOperand())) {
239239
if (!hasOwnership()) {
240-
return Builder.createUncheckedAddrCast(PTAI->getLoc(), ATPI->getOperand(),
240+
return Builder.createUncheckedAddrCast(PTAI->getLoc(),
241+
ATPI->getOperand(),
241242
PTAI->getType());
242243
}
243244

244-
OwnershipRAUWHelper helper(ownershipFixupContext, PTAI, ATPI->getOperand());
245+
OwnershipRAUWHelper helper(ownershipFixupContext, PTAI,
246+
ATPI->getOperand());
245247
if (helper) {
246-
auto *newInst = Builder.createUncheckedAddrCast(PTAI->getLoc(), ATPI->getOperand(),
248+
auto replacement = helper.prepareReplacement();
249+
auto *newInst = Builder.createUncheckedAddrCast(PTAI->getLoc(),
250+
replacement,
247251
PTAI->getType());
248252
helper.perform(newInst);
249253
return nullptr;
@@ -726,8 +730,9 @@ SILCombiner::visitRawPointerToRefInst(RawPointerToRefInst *rawToRef) {
726730
// Since we are using std::next, we use getAutogeneratedLocation to
727731
// avoid any issues if our next insertion point is a terminator.
728732
auto loc = RegularLocation::getAutoGeneratedLocation();
733+
auto replacement = helper.prepareReplacement();
729734
auto *newInst = localBuilder.createUncheckedRefCast(
730-
loc, originalRef, rawToRef->getType());
735+
loc, replacement, rawToRef->getType());
731736
// If we have an operand with ownership, we need to change our
732737
// unchecked_ref_cast to produce an unowned value. This is because
733738
// otherwise, our unchecked_ref_cast will consume the underlying owned
@@ -800,8 +805,9 @@ visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *UBCI) {
800805

801806
OwnershipRAUWHelper helper(ownershipFixupContext, UBCI, Oper);
802807
if (helper) {
808+
auto replacement = helper.prepareReplacement();
803809
auto *transformedOper = Builder.createUncheckedBitwiseCast(
804-
UBCI->getLoc(), Oper, UBCI->getType());
810+
UBCI->getLoc(), replacement, UBCI->getType());
805811
helper.perform(transformedOper);
806812
return nullptr;
807813
}
@@ -817,31 +823,21 @@ visitUncheckedBitwiseCastInst(UncheckedBitwiseCastInst *UBCI) {
817823
Builder.getModule()))
818824
return nullptr;
819825

820-
if (!Builder.hasOwnership()) {
821-
return Builder.createUncheckedRefCast(UBCI->getLoc(), UBCI->getOperand(),
822-
UBCI->getType());
826+
// Normally, OwnershipRAUWHelper needs to be called to handle ownership of
827+
// UBCI->getOperand(). However, we know that UBCI->getOperand() is already
828+
// available at the point of the cast, and by forcing the cast to be Unowned,
829+
// we ensure that no ownership adjustment is needed. So we can skip
830+
// prepareReplacement completely and just drop in the replacement. That avoids
831+
// an extra copy in the case that UBCI->getOperand() is Owned.
832+
auto *refCast = Builder.createUncheckedRefCast(
833+
UBCI->getLoc(), UBCI->getOperand(), UBCI->getType());
834+
if (Builder.hasOwnership()) {
835+
// A bitwise cast is always unowned, so we can safely force the reference
836+
// cast to forward as unowned and no ownership adjustment is needed.
837+
assert(UBCI->getOwnershipKind() == OwnershipKind::Unowned);
838+
refCast->setForwardingOwnershipKind(OwnershipKind::Unowned);
823839
}
824-
825-
{
826-
OwnershipRAUWHelper helper(ownershipFixupContext, UBCI, UBCI->getOperand());
827-
if (helper) {
828-
auto *newInst = Builder.createUncheckedRefCast(UBCI->getLoc(), UBCI->getOperand(),
829-
UBCI->getType());
830-
// If we have an operand with owned ownership, we change our
831-
// unchecked_ref_cast to explicitly pass the owned operand as an unowned
832-
// value. This is because otherwise, we would consume the owned value
833-
// creating breaking OSSA. In contrast, if we have a guaranteed value, we
834-
// are going to be replacing an UnownedInstantaneousUse with an
835-
// InstantaneousUse which is always safe for a guaranteed value.
836-
if (newInst->getForwardingOwnershipKind() == OwnershipKind::Owned) {
837-
newInst->setForwardingOwnershipKind(OwnershipKind::Unowned);
838-
}
839-
helper.perform(newInst);
840-
return nullptr;
841-
}
842-
}
843-
844-
return nullptr;
840+
return refCast;
845841
}
846842

847843
SILInstruction *
@@ -1064,10 +1060,11 @@ SILCombiner::visitConvertFunctionInst(ConvertFunctionInst *cfi) {
10641060
continue;
10651061
}
10661062

1067-
OwnershipRAUWHelper helper(ownershipFixupContext, pa,
1068-
cfi->getConverted());
1069-
if (!helper)
1063+
OwnershipRAUWHelper checkRAUW(ownershipFixupContext, pa,
1064+
cfi->getConverted());
1065+
if (!checkRAUW)
10701066
continue;
1067+
10711068
SmallVector<SILValue, 4> args(pa->getArguments().begin(),
10721069
pa->getArguments().end());
10731070
auto newValue = makeCopiedValueAvailable(cfi->getConverted(),
@@ -1085,7 +1082,12 @@ SILCombiner::visitConvertFunctionInst(ConvertFunctionInst *cfi) {
10851082
// We need to end the lifetime of the convert_function/partial_apply since
10861083
// the helper assumes that ossa is correct upon input.
10871084
localBuilder.emitDestroyValueOperation(pa->getLoc(), newConvert);
1088-
helper.perform(newConvert);
1085+
// 'newConvert' may have different ownership than then 'cfi'. newConvert
1086+
// is always owned, while 'cfi' may have been guaranteed. OSSA-RAUW
1087+
// validity depends on the ownership kind. Reinstantiate
1088+
// OwnershipRAUWHelper to verify that it is still valid
1089+
// (a very fast check in this case).
1090+
OwnershipRAUWHelper(ownershipFixupContext, pa, newConvert).perform();
10891091
}
10901092
}
10911093

0 commit comments

Comments
 (0)