Skip to content

Commit a2e0964

Browse files
authored
Merge pull request #39721 from atrick/oou-cleanup
OSSA RAUW cleanup in preparation for fixes and features
2 parents b69bffc + 49ce692 commit a2e0964

File tree

5 files changed

+300
-169
lines changed

5 files changed

+300
-169
lines changed

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,13 +647,35 @@ FullApplySite cloneFullApplySiteReplacingCallee(FullApplySite applySite,
647647
SILValue newCallee,
648648
SILBuilderContext &builderCtx);
649649

650+
/// Replace all uses of \p oldValue with \p newValue, notifying the callbacks
651+
/// of new uses and when end-of-scope instructions are deleted.
652+
SILBasicBlock::iterator replaceAllUses(SILValue oldValue, SILValue newValue,
653+
SILBasicBlock::iterator nextii,
654+
InstModCallbacks &callbacks);
655+
650656
/// This is a low level routine that makes all uses of \p svi uses of \p
651657
/// newValue (ignoring end scope markers) and then deletes \p svi and all end
652658
/// scope markers. Then returns the next inst to process.
653659
SILBasicBlock::iterator replaceAllUsesAndErase(SingleValueInstruction *svi,
654660
SILValue newValue,
655661
InstModCallbacks &callbacks);
656662

663+
/// Replace all uses of \p oldValue with \p newValue, delete the instruction
664+
/// that defines \p oldValue, and notify the callbacks of new uses and when
665+
/// the defining instruction and its end-of-scope instructions are deleted.
666+
///
667+
/// Precondition: \p oldValue must be a SingleValueInstruction or a terminator
668+
/// result. \p oldValue must be the only result with remaining uses. For
669+
/// terminators with multiple results, remove all other results for, e.g. via
670+
/// replaceAllUsesWithUndef().
671+
///
672+
/// If \p oldValue is a terminator result, a new branch instruction is inserted
673+
/// in place of the old terminator and all basic block successors become
674+
/// unreachable except for the successor containing the replaced result.
675+
SILBasicBlock::iterator replaceAllUsesAndErase(SILValue oldValue,
676+
SILValue newValue,
677+
InstModCallbacks &callbacks);
678+
657679
/// This API is equivalent to performing \p use->set(\p newValue) except that:
658680
///
659681
/// 1. If the user of \p use is an end scope, this API no-opts. This API is only

include/swift/SILOptimizer/Utils/OwnershipOptUtils.h

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,29 +104,46 @@ struct OwnershipFixupContext {
104104
}
105105
};
106106

107-
/// A utility composed ontop of OwnershipFixupContext that knows how to RAUW a
107+
/// A utility composed on top of OwnershipFixupContext that knows how to RAUW a
108108
/// value or a single value instruction with a new value and then fixup
109109
/// ownership invariants afterwards.
110110
class OwnershipRAUWHelper {
111+
public:
112+
/// Return true if \p oldValue can be replaced with \p newValue in terms of
113+
/// their value ownership. This ignores any current uses of \p oldValue. To
114+
/// determine whether \p oldValue can be replaced as-is with it's existing
115+
/// uses, create an instance of OwnershipRAUWHelper and check its validity.
116+
static bool hasValidRAUWOwnership(SILValue oldValue, SILValue newValue);
117+
118+
private:
111119
OwnershipFixupContext *ctx;
112-
SingleValueInstruction *oldValue;
120+
SILValue oldValue;
113121
SILValue newValue;
114122

115123
public:
116-
OwnershipRAUWHelper() : ctx(nullptr), oldValue(nullptr), newValue(nullptr) {}
124+
OwnershipRAUWHelper() : ctx(nullptr) {}
125+
126+
~OwnershipRAUWHelper() { if (ctx) ctx->clear(); }
117127

118128
/// Return an instance of this class if we can perform the specific RAUW
119129
/// operation ignoring if the types line up. Returns None otherwise.
120130
///
131+
/// \p oldValue may be either a SingleValueInstruction or a terminator result.
132+
///
133+
/// Precondition: If \p oldValue is a BorrowedValue that introduces a local
134+
/// borrow scope, then \p newValue must either be defined in the same block as
135+
/// \p oldValue, or it must dominate \p oldValue (rather than merely
136+
/// dominating its uses).
137+
///
121138
/// DISCUSSION: We do not check that the types line up here so that we can
122139
/// allow for our users to transform our new value in ways that preserve
123140
/// ownership at \p oldValue before we perform the actual RAUW. If \p newValue
124141
/// is an object, any instructions in the chain of transforming instructions
125142
/// from \p newValue at \p oldValue's must be forwarding. If \p newValue is an
126143
/// address, then these transforms can only transform the address into a
127144
/// derived address.
128-
OwnershipRAUWHelper(OwnershipFixupContext &ctx,
129-
SingleValueInstruction *oldValue, SILValue newValue);
145+
OwnershipRAUWHelper(OwnershipFixupContext &ctx, SILValue oldValue,
146+
SILValue newValue);
130147

131148
/// Returns true if this helper was initialized into a valid state.
132149
operator bool() const { return isValid(); }
@@ -149,7 +166,6 @@ class OwnershipRAUWHelper {
149166
SILValue newValue);
150167

151168
void invalidate() {
152-
ctx->clear();
153169
ctx = nullptr;
154170
}
155171
};
@@ -158,6 +174,9 @@ class OwnershipRAUWHelper {
158174
/// a single use of a value with another value with a different ownership. We
159175
/// allow for the values to have different types.
160176
///
177+
/// Precondition: if \p use ends a borrow scope, then \p newValue dominates the
178+
/// BorrowedValue that begins the scope.
179+
///
161180
/// NOTE: When not in OSSA, this just performs a normal set use, so this code is
162181
/// safe to use with all code.
163182
class OwnershipReplaceSingleUseHelper {
@@ -177,6 +196,8 @@ class OwnershipReplaceSingleUseHelper {
177196
OwnershipReplaceSingleUseHelper(OwnershipFixupContext &ctx, Operand *use,
178197
SILValue newValue);
179198

199+
~OwnershipReplaceSingleUseHelper() { if (ctx) ctx->clear(); }
200+
180201
/// Returns true if this helper was initialized into a valid state.
181202
operator bool() const { return isValid(); }
182203
bool isValid() const { return bool(ctx) && bool(use) && bool(newValue); }

lib/SILOptimizer/Transforms/SimplifyCFG.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4018,6 +4018,8 @@ bool SimplifyCFG::simplifyArgument(SILBasicBlock *BB, unsigned i) {
40184018
return true;
40194019
}
40204020

4021+
// OWNERSHIP NOTE: This is always safe for guaranteed and owned arguments since
4022+
// in both cases the phi will consume its input.
40214023
static void tryToReplaceArgWithIncomingValue(SILBasicBlock *BB, unsigned i,
40224024
DominanceInfo *DT) {
40234025
auto *A = BB->getArgument(i);

lib/SILOptimizer/Utils/InstOptUtils.cpp

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1925,15 +1925,19 @@ swift::cloneFullApplySiteReplacingCallee(FullApplySite applySite,
19251925
llvm_unreachable("Unhandled case?!");
19261926
}
19271927

1928-
SILBasicBlock::iterator
1929-
swift::replaceAllUsesAndErase(SingleValueInstruction *svi, SILValue newValue,
1930-
InstModCallbacks &callbacks) {
1931-
assert(svi != newValue && "Cannot RAUW a value with itself");
1932-
SILBasicBlock::iterator nextii = std::next(svi->getIterator());
1933-
1934-
// Only SingleValueInstructions are currently simplified.
1935-
while (!svi->use_empty()) {
1936-
Operand *use = *svi->use_begin();
1928+
// FIXME: For any situation where this may be called on an unbounded number of
1929+
// uses, it should perform a single callback invocation to notify the client
1930+
// that newValue has new uses rather than a callback for every new use.
1931+
//
1932+
// FIXME: This should almost certainly replace end_lifetime uses rather than
1933+
// deleting them.
1934+
SILBasicBlock::iterator swift::replaceAllUses(SILValue oldValue,
1935+
SILValue newValue,
1936+
SILBasicBlock::iterator nextii,
1937+
InstModCallbacks &callbacks) {
1938+
assert(oldValue != newValue && "Cannot RAUW a value with itself");
1939+
while (!oldValue->use_empty()) {
1940+
Operand *use = *oldValue->use_begin();
19371941
SILInstruction *user = use->getUser();
19381942
// Erase the end of scope marker.
19391943
if (isEndOfScopeMarker(user)) {
@@ -1944,12 +1948,62 @@ swift::replaceAllUsesAndErase(SingleValueInstruction *svi, SILValue newValue,
19441948
}
19451949
callbacks.setUseValue(use, newValue);
19461950
}
1951+
return nextii;
1952+
}
1953+
1954+
SILBasicBlock::iterator
1955+
swift::replaceAllUsesAndErase(SingleValueInstruction *svi, SILValue newValue,
1956+
InstModCallbacks &callbacks) {
1957+
SILBasicBlock::iterator nextii = replaceAllUses(
1958+
svi, newValue, std::next(svi->getIterator()), callbacks);
19471959

19481960
callbacks.deleteInst(svi);
19491961

19501962
return nextii;
19511963
}
19521964

1965+
SILBasicBlock::iterator
1966+
swift::replaceAllUsesAndErase(SILValue oldValue, SILValue newValue,
1967+
InstModCallbacks &callbacks) {
1968+
auto *blockArg = dyn_cast<SILPhiArgument>(oldValue);
1969+
if (!blockArg) {
1970+
// SingleValueInstruction SSA replacement.
1971+
return replaceAllUsesAndErase(cast<SingleValueInstruction>(oldValue),
1972+
newValue, callbacks);
1973+
}
1974+
llvm_unreachable("Untested");
1975+
#if 0 // FIXME: to be enabled in a following commit
1976+
TermInst *oldTerm = blockArg->getTerminatorForResult();
1977+
assert(oldTerm && "can only replace and erase terminators, not phis");
1978+
1979+
// Before:
1980+
// oldTerm bb1, bb2
1981+
// bb1(%oldValue):
1982+
// use %oldValue
1983+
// bb2:
1984+
//
1985+
// After:
1986+
// br bb1
1987+
// bb1:
1988+
// use %newValue
1989+
// bb2:
1990+
1991+
auto nextii = replaceAllUses(blockArg, newValue,
1992+
oldTerm->getParent()->end(), callbacks);
1993+
// Now that oldValue is replaced, the terminator should have no uses
1994+
// left. The caller should have removed uses from other results.
1995+
for (auto *succBB : oldTerm->getSuccessorBlocks()) {
1996+
assert(succBB->getNumArguments() == 1 && "expected terminator result");
1997+
succBB->eraseArgument(0);
1998+
}
1999+
auto *newBr = SILBuilderWithScope(oldTerm).createBranch(
2000+
oldTerm->getLoc(), blockArg->getParent());
2001+
callbacks.createdNewInst(newBr);
2002+
callbacks.deleteInst(oldTerm);
2003+
return nextii;
2004+
#endif
2005+
}
2006+
19532007
/// Given that we are going to replace use's underlying value, if the use is a
19542008
/// lifetime ending use, insert an end scope scope use for the underlying value
19552009
/// before we RAUW.

0 commit comments

Comments
 (0)