Skip to content

Commit 652ff78

Browse files
committed
OSSA RAUW helper redesign - split prepareReplacement() vs. perform()
Required to fix SILCombine. Divide the logic into smaller pieces. This allows passes to check for replaceability before generating the replacement value. Preparation for simplifying OSSA utilities into smaller logical components making them flexibile and allowing improvements to be staged in.
1 parent 4a85e18 commit 652ff78

File tree

2 files changed

+68
-63
lines changed

2 files changed

+68
-63
lines changed

include/swift/SILOptimizer/Utils/OwnershipOptUtils.h

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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/Utils/OwnershipOptUtils.cpp

Lines changed: 51 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -953,11 +953,14 @@ OwnershipLifetimeExtender::borrowOverSingleUse(SILValue newValue,
953953
/// invariants. We leave fixing up the lifetime of old value to our caller.
954954
namespace {
955955

956-
struct OwnershipRAUWUtility {
956+
struct OwnershipRAUWPrepare {
957957
SILValue oldValue;
958-
SILValue newValue;
959958
OwnershipFixupContext &ctx;
960959

960+
OwnershipLifetimeExtender getLifetimeExtender() { return {ctx}; }
961+
962+
const InstModCallbacks &getCallbacks() const { return ctx.callbacks; }
963+
961964
// For terminator results, the consuming point is the predecessor's
962965
// terminator. This avoids destroys on unused paths. It is also the
963966
// instruction which will be deleted, thus needs operand cleanup.
@@ -968,18 +971,15 @@ struct OwnershipRAUWUtility {
968971
return cast<SingleValueInstruction>(oldValue);
969972
}
970973

971-
SILBasicBlock::iterator handleUnowned();
972-
973-
SILBasicBlock::iterator perform();
974-
975-
OwnershipLifetimeExtender getLifetimeExtender() { return {ctx}; }
974+
SILValue prepareReplacement(SILValue newValue);
976975

977-
const InstModCallbacks &getCallbacks() const { return ctx.callbacks; }
976+
private:
977+
SILValue prepareUnowned(SILValue newValue);
978978
};
979979

980980
} // anonymous namespace
981981

982-
SILBasicBlock::iterator OwnershipRAUWUtility::handleUnowned() {
982+
SILValue OwnershipRAUWPrepare::prepareUnowned(SILValue newValue) {
983983
auto &callbacks = ctx.callbacks;
984984
switch (newValue.getOwnershipKind()) {
985985
case OwnershipKind::None:
@@ -988,15 +988,15 @@ SILBasicBlock::iterator OwnershipRAUWUtility::handleUnowned() {
988988
llvm_unreachable("Invalid for values");
989989
case OwnershipKind::Unowned:
990990
// An unowned value can always be RAUWed with another unowned value.
991-
return replaceAllUsesAndErase(oldValue, newValue, callbacks);
991+
return newValue;
992992
case OwnershipKind::Guaranteed: {
993993
// If we have an unowned value that we want to replace with a guaranteed
994994
// value, we need to ensure that the guaranteed value is live at all use
995995
// points of the unowned value. If so, just replace and continue.
996996
//
997997
// TODO: Implement this for more interesting cases.
998998
if (isa<SILFunctionArgument>(newValue))
999-
return replaceAllUsesAndErase(oldValue, newValue, callbacks);
999+
return newValue;
10001000

10011001
// Otherwise, we need to lifetime extend the borrow over all of the use
10021002
// points. To do so, we copy the value, borrow it, and insert an unchecked
@@ -1024,7 +1024,7 @@ SILBasicBlock::iterator OwnershipRAUWUtility::handleUnowned() {
10241024
auto borrowPt = getBorrowPoint(newValue, oldValue);
10251025
SILValue borrow = extender.borrowCopyOverGuaranteedUses(
10261026
newValue, borrowPt, oldValue->getUses());
1027-
return replaceAllUsesAndErase(oldValue, borrow, callbacks);
1027+
return borrow;
10281028
}
10291029
case OwnershipKind::Owned: {
10301030
// If we have an unowned value that we want to replace with an owned value,
@@ -1053,23 +1053,23 @@ SILBasicBlock::iterator OwnershipRAUWUtility::handleUnowned() {
10531053
}
10541054
auto extender = getLifetimeExtender();
10551055
SILValue copy = extender.createPlusZeroCopy(newValue, oldValue->getUses());
1056-
auto result = replaceAllUsesAndErase(oldValue, copy, callbacks);
1057-
return result;
1056+
return copy;
10581057
}
10591058
}
10601059
llvm_unreachable("covered switch isn't covered?!");
10611060
}
10621061

1063-
SILBasicBlock::iterator OwnershipRAUWUtility::perform() {
1062+
SILValue OwnershipRAUWPrepare::prepareReplacement(SILValue newValue) {
10641063
assert(oldValue->getFunction()->hasOwnership());
10651064
assert(OwnershipRAUWHelper::hasValidRAUWOwnership(oldValue, newValue) &&
10661065
"Should have checked if can perform this operation before calling it?!");
1067-
// If our new value is just none, we can pass anything to do it so just RAUW
1066+
// If our new value is just none, we can pass anything to it so just RAUW
10681067
// and return.
10691068
//
10701069
// NOTE: This handles RAUWing with undef.
10711070
if (newValue.getOwnershipKind() == OwnershipKind::None)
1072-
return replaceAllUsesAndErase(oldValue, newValue, ctx.callbacks);
1071+
return newValue;
1072+
10731073
assert(oldValue.getOwnershipKind() != OwnershipKind::None);
10741074

10751075
switch (oldValue.getOwnershipKind()) {
@@ -1081,9 +1081,7 @@ SILBasicBlock::iterator OwnershipRAUWUtility::perform() {
10811081
case OwnershipKind::Any:
10821082
llvm_unreachable("Invalid for values");
10831083
case OwnershipKind::Guaranteed: {
1084-
auto extender = getLifetimeExtender();
1085-
SILValue newGuaranteedValue = extender.borrowOverValue(newValue, oldValue);
1086-
return replaceAllUsesAndErase(oldValue, newGuaranteedValue, ctx.callbacks);
1084+
return getLifetimeExtender().borrowOverValue(newValue, oldValue);
10871085
}
10881086
case OwnershipKind::Owned: {
10891087
// If we have an owned value that we want to replace with a value with any
@@ -1095,11 +1093,10 @@ SILBasicBlock::iterator OwnershipRAUWUtility::perform() {
10951093
SILValue copy = extender.createPlusOneCopy(newValue, consumingPoint);
10961094

10971095
cleanupOperandsBeforeDeletion(consumingPoint, ctx.callbacks);
1098-
1099-
return replaceAllUsesAndErase(oldValue, copy, ctx.callbacks);
1096+
return copy;
11001097
}
11011098
case OwnershipKind::Unowned: {
1102-
return handleUnowned();
1099+
return prepareUnowned(newValue);
11031100
}
11041101
}
11051102
llvm_unreachable("Covered switch isn't covered?!");
@@ -1109,20 +1106,21 @@ SILBasicBlock::iterator OwnershipRAUWUtility::perform() {
11091106
// Interior Pointer Operand Rebasing
11101107
//===----------------------------------------------------------------------===//
11111108

1112-
SILBasicBlock::iterator
1113-
OwnershipRAUWHelper::replaceAddressUses(SingleValueInstruction *oldValue,
1114-
SILValue newValue) {
1109+
/// Return an address equivalent to \p newValue that can be used to replace all
1110+
/// uses of \p oldValue.
1111+
///
1112+
/// Precondition: RAUW of two addresses
1113+
SILValue
1114+
OwnershipRAUWHelper::getReplacementAddress() {
11151115
assert(oldValue->getType().isAddress() && newValue->getType().isAddress());
11161116

11171117
// If newValue was not generated by an interior pointer, then it cannot
11181118
// be within a borrow scope, so direct replacement works.
11191119
if (!requiresCopyBorrowAndClone())
1120-
return replaceAllUsesAndErase(oldValue, newValue, ctx->callbacks);
1120+
return newValue;
11211121

1122-
// We are RAUWing two addresses and we found that:
1123-
//
1124-
// 1. newValue is an address associated with an interior pointer instruction.
1125-
// 2. oldValue has uses that are outside of newValue's borrow scope.
1122+
// newValue may be within a borrow scope, and oldValue may have uses that are
1123+
// outside of newValue's borrow scope.
11261124
//
11271125
// So, we need to copy/borrow the base value of the interior pointer to
11281126
// lifetime extend the base value over the new uses. Then we clone the
@@ -1151,12 +1149,9 @@ OwnershipRAUWHelper::replaceAddressUses(SingleValueInstruction *oldValue,
11511149
auto checkBase = [&](SILValue srcAddr) {
11521150
return (srcAddr == refProjection) ? SILValue(newBase) : SILValue();
11531151
};
1154-
SILValue clonedAddr =
1155-
cloneUseDefChain(newValue, /*insetPt*/ oldValue, checkBase);
1152+
SILValue clonedAddr = cloneUseDefChain(newValue, bbiNext, checkBase);
11561153
assert(clonedAddr != newValue && "expect at least the base to be replaced");
1157-
1158-
// Now that we have an addr that is setup appropriately, RAUW!
1159-
return replaceAllUsesAndErase(oldValue, clonedAddr, ctx->callbacks);
1154+
return clonedAddr;
11601155
}
11611156

11621157
//===----------------------------------------------------------------------===//
@@ -1286,14 +1281,10 @@ OwnershipRAUWHelper::OwnershipRAUWHelper(OwnershipFixupContext &inputCtx,
12861281
}
12871282
}
12881283

1289-
SILBasicBlock::iterator
1290-
OwnershipRAUWHelper::perform(SingleValueInstruction *maybeTransformedNewValue) {
1284+
SILValue OwnershipRAUWHelper::prepareReplacement(SILValue rewrittenNewValue) {
12911285
assert(isValid() && "OwnershipRAUWHelper invalid?!");
12921286

1293-
SILValue actualNewValue = newValue;
1294-
if (maybeTransformedNewValue) {
1295-
// Temporary variable for rebasing
1296-
SILValue rewrittenNewValue = maybeTransformedNewValue;
1287+
if (rewrittenNewValue) {
12971288
// Everything about \n newValue that the constructor checks should also be
12981289
// true for rewrittenNewValue.
12991290
// FIXME: enable these...
@@ -1304,27 +1295,33 @@ OwnershipRAUWHelper::perform(SingleValueInstruction *maybeTransformedNewValue) {
13041295
assert(!newValue->getType().isAddress() ||
13051296
AddressOwnership(rewrittenNewValue) == AddressOwnership(newValue));
13061297

1307-
actualNewValue = maybeTransformedNewValue;
1308-
1309-
// TODO: newValue = rewrittenNewValue; (remove actualNewValue)
1298+
newValue = rewrittenNewValue;
13101299
}
13111300
assert(newValue && "prepareReplacement can only be called once");
13121301
SWIFT_DEFER { newValue = SILValue(); };
13131302

13141303
if (!oldValue->getFunction()->hasOwnership())
1315-
return replaceAllUsesAndErase(oldValue, actualNewValue, ctx->callbacks);
1304+
return newValue;
1305+
1306+
if (oldValue->getType().isAddress()) {
1307+
return getReplacementAddress();
1308+
}
1309+
OwnershipRAUWPrepare rauwPrepare{oldValue, *ctx};
1310+
return rauwPrepare.prepareReplacement(newValue);
1311+
}
1312+
1313+
SILBasicBlock::iterator
1314+
OwnershipRAUWHelper::perform(SILValue replacementValue) {
1315+
if (!replacementValue)
1316+
replacementValue = prepareReplacement();
1317+
1318+
assert(!newValue && "prepareReplacement() must be called");
13161319

13171320
// Make sure to always clear our context after we transform.
13181321
SWIFT_DEFER { ctx->clear(); };
13191322

1320-
if (oldValue->getType().isAddress()) {
1321-
assert(isa<SingleValueInstruction>(oldValue)
1322-
&& "block argument cannot be an address");
1323-
return replaceAddressUses(cast<SingleValueInstruction>(oldValue),
1324-
actualNewValue);
1325-
}
1326-
OwnershipRAUWUtility utility{oldValue, actualNewValue, *ctx};
1327-
return utility.perform();
1323+
auto *svi = dyn_cast<SingleValueInstruction>(oldValue);
1324+
return replaceAllUsesAndErase(svi, replacementValue, ctx->callbacks);
13281325
}
13291326

13301327
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)