Skip to content

Commit 2243ffe

Browse files
authored
Merge pull request #35461 from gottesmm/ossa-interior-ptr-fixup
[ownership] Implement Interior Pointer handling API for RAUWing addresses
2 parents 1329a9c + e4e1689 commit 2243ffe

19 files changed

+853
-170
lines changed

include/swift/SIL/InstructionUtils.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ SILValue getUnderlyingObject(SILValue V);
2727

2828
SILValue getUnderlyingObjectStopAtMarkDependence(SILValue V);
2929

30+
/// Given an address look through address to address projections and indexing
31+
/// insts.
32+
SILValue getUnderlyingObjectStoppingAtObjectToAddrProjections(SILValue v);
33+
3034
SILValue stripSinglePredecessorArgs(SILValue V);
3135

3236
/// Return the underlying SILValue after stripping off all casts from the
@@ -55,8 +59,14 @@ SILValue stripClassCasts(SILValue V);
5559

5660
/// Return the underlying SILValue after stripping off all address projection
5761
/// instructions.
62+
///
63+
/// FIXME: Today address projections are referring to the result of the
64+
/// projection and doesn't consider the operand. Should we change this?
5865
SILValue stripAddressProjections(SILValue V);
5966

67+
/// Look through any projections that transform an address -> an address.
68+
SILValue lookThroughAddressToAddressProjections(SILValue v);
69+
6070
/// Return the underlying SILValue after stripping off all aggregate projection
6171
/// instructions.
6272
///

include/swift/SIL/OwnershipUtils.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,17 @@ struct InteriorPointerOperand {
672672
/// requirements to ensure that the underlying class is alive at all use
673673
/// points.
674674
bool getImplicitUses(SmallVectorImpl<Operand *> &foundUses,
675-
std::function<void(Operand *)> *onError = nullptr);
675+
std::function<void(Operand *)> *onError = nullptr) {
676+
return getImplicitUsesForAddress(getProjectedAddress(), foundUses, onError);
677+
}
678+
679+
/// The algorithm that is used to determine what the verifier will consider to
680+
/// be implicit uses of the given address. Used to implement \see
681+
/// getImplicitUses.
682+
static bool
683+
getImplicitUsesForAddress(SILValue address,
684+
SmallVectorImpl<Operand *> &foundUses,
685+
std::function<void(Operand *)> *onError = nullptr);
676686

677687
Operand *operator->() { return operand; }
678688
const Operand *operator->() const { return operand; }

include/swift/SIL/Projection.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,10 @@ class Projection {
391391
}
392392
}
393393

394+
static bool isAddressToAddressProjection(SILValue v) {
395+
return isAddressProjection(v) && !isObjectToAddressProjection(v);
396+
}
397+
394398
/// Returns true if this instruction projects from an object type into an
395399
/// address subtype.
396400
static bool isObjectToAddressProjection(SILValue V) {

include/swift/SILOptimizer/Analysis/SimplifyInstruction.h

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,31 +27,30 @@ namespace swift {
2727
class SILInstruction;
2828
class InstModCallbacks;
2929

30-
/// Try to simplify the specified instruction, performing local
31-
/// analysis of the operands of the instruction, without looking at its uses
32-
/// (e.g. constant folding). If a simpler result can be found, it is
33-
/// returned, otherwise a null SILValue is returned.
34-
///
35-
/// This is assumed to implement read-none transformations.
36-
SILValue simplifyInstruction(SILInstruction *I);
37-
3830
/// Replace an instruction with a simplified result and erase it. If the
3931
/// instruction initiates a scope, do not replace the end of its scope; it will
4032
/// be deleted along with its parent.
4133
///
42-
/// If it is nonnull, eraseNotify will be called before each instruction is
43-
/// deleted.
44-
///
45-
/// If it is nonnull and inst is in OSSA, newInstNotify will be called with each
46-
/// new instruction inserted to compensate for ownership.
47-
///
4834
/// NOTE: When OSSA is enabled this API assumes OSSA is properly formed and will
4935
/// insert compensating instructions.
5036
SILBasicBlock::iterator
5137
replaceAllSimplifiedUsesAndErase(SILInstruction *I, SILValue result,
5238
InstModCallbacks &callbacks,
5339
DeadEndBlocks *deadEndBlocks = nullptr);
5440

41+
/// Attempt to map \p inst to a simplified result. Upon success, replace \p inst
42+
/// with this simplified result and erase \p inst. If the instruction initiates
43+
/// a scope, do not replace the end of its scope; it will be deleted along with
44+
/// its parent.
45+
///
46+
/// NOTE: When OSSA is enabled this API assumes OSSA is properly formed and will
47+
/// insert compensating instructions.
48+
/// NOTE: When \p I is in an OSSA function, this fails to optimize if \p
49+
/// deadEndBlocks is null.
50+
SILBasicBlock::iterator simplifyAndReplaceAllSimplifiedUsesAndErase(
51+
SILInstruction *I, InstModCallbacks &callbacks,
52+
DeadEndBlocks *deadEndBlocks = nullptr);
53+
5554
// Simplify invocations of builtin operations that may overflow.
5655
/// All such operations return a tuple (result, overflow_flag).
5756
/// This function try to simplify such operations, but returns only a

include/swift/SILOptimizer/Utils/InstOptUtils.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,10 @@ class InstModCallbacks {
400400
}
401401

402402
bool hadCallbackInvocation() const { return wereAnyCallbacksInvoked; }
403+
404+
/// Set \p wereAnyCallbacksInvoked to false. Useful if one wants to reuse an
405+
/// InstModCallback in between iterations.
406+
void resetHadCallbackInvocation() { wereAnyCallbacksInvoked = false; }
403407
};
404408

405409
/// Get all consumed arguments of a partial_apply.

include/swift/SILOptimizer/Utils/OwnershipOptUtils.h

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#ifndef SWIFT_SILOPTIMIZER_UTILS_OWNERSHIPOPTUTILS_H
2020
#define SWIFT_SILOPTIMIZER_UTILS_OWNERSHIPOPTUTILS_H
2121

22+
#include "swift/SIL/BasicBlockUtils.h"
2223
#include "swift/SIL/OwnershipUtils.h"
2324
#include "swift/SIL/SILModule.h"
2425
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
@@ -28,31 +29,98 @@ namespace swift {
2829
// Defined in BasicBlockUtils.h
2930
struct JointPostDominanceSetComputer;
3031

32+
/// A struct that contains context shared in between different operation +
33+
/// "ownership fixup" utilities. Please do not put actual methods on this, it is
34+
/// meant to be composed with.
3135
struct OwnershipFixupContext {
3236
Optional<InstModCallbacks> inlineCallbacks;
3337
InstModCallbacks &callbacks;
3438
DeadEndBlocks &deBlocks;
3539
JointPostDominanceSetComputer &jointPostDomSetComputer;
3640

41+
/// Extra state initialized by OwnershipRAUWFixupHelper::get() that we use
42+
/// when RAUWing addresses. This ensures we do not need to recompute this
43+
/// state when we perform the actual RAUW.
44+
struct AddressFixupContext {
45+
/// When determining if we need to perform an address pointer fixup, we
46+
/// compute all transitive address uses of oldValue. If we find that we do
47+
/// need this fixed up, then we will copy our interior pointer base value
48+
/// and use this to seed that new lifetime.
49+
SmallVector<Operand *, 8> allAddressUsesFromOldValue;
50+
51+
/// This is the interior pointer operand that the new value we want to RAUW
52+
/// is transitively derived from and enables us to know the underlying
53+
/// borrowed base value that we need to lifetime extend.
54+
InteriorPointerOperand intPtrOp;
55+
56+
void clear() {
57+
allAddressUsesFromOldValue.clear();
58+
intPtrOp = InteriorPointerOperand();
59+
}
60+
};
61+
AddressFixupContext extraAddressFixupInfo;
62+
3763
OwnershipFixupContext(InstModCallbacks &callbacks, DeadEndBlocks &deBlocks,
38-
JointPostDominanceSetComputer &inputJPDComputer)
39-
: inlineCallbacks(), callbacks(callbacks), deBlocks(deBlocks),
40-
jointPostDomSetComputer(inputJPDComputer) {}
64+
JointPostDominanceSetComputer &jointPostDomSetComputer)
65+
: callbacks(callbacks), deBlocks(deBlocks),
66+
jointPostDomSetComputer(jointPostDomSetComputer) {}
67+
68+
void clear() {
69+
jointPostDomSetComputer.clear();
70+
extraAddressFixupInfo.allAddressUsesFromOldValue.clear();
71+
extraAddressFixupInfo.intPtrOp = InteriorPointerOperand();
72+
}
73+
74+
private:
75+
/// Helper method called to determine if we discovered we needed interior
76+
/// pointer fixups while simplifying.
77+
bool needsInteriorPointerFixups() const {
78+
return bool(extraAddressFixupInfo.intPtrOp);
79+
}
80+
};
4181

42-
OwnershipFixupContext(DeadEndBlocks &deBlocks,
43-
JointPostDominanceSetComputer &inputJPDComputer)
44-
: inlineCallbacks(InstModCallbacks()), callbacks(*inlineCallbacks),
45-
deBlocks(deBlocks), jointPostDomSetComputer(inputJPDComputer) {}
82+
/// A utility composed ontop of OwnershipFixupContext that knows how to RAUW a
83+
/// value or a single value instruction with a new value and then fixup
84+
/// ownership invariants afterwards.
85+
class OwnershipRAUWHelper {
86+
OwnershipFixupContext *ctx;
87+
SingleValueInstruction *oldValue;
88+
SILValue newValue;
4689

47-
SILBasicBlock::iterator
48-
replaceAllUsesAndErase(SingleValueInstruction *oldValue, SILValue newValue);
90+
public:
91+
OwnershipRAUWHelper() : ctx(nullptr), oldValue(nullptr), newValue(nullptr) {}
4992

50-
/// We can not RAUW all old values with new values.
93+
/// Return an instance of this class if we can perform the specific RAUW
94+
/// operation ignoring if the types line up. Returns None otherwise.
5195
///
52-
/// Namely, we do not support RAUWing values with ValueOwnershipKind::None
53-
/// that have uses that do not require ValueOwnershipKind::None or
54-
/// ValueOwnershipKind::Any.
55-
static bool canFixUpOwnershipForRAUW(SILValue oldValue, SILValue newValue);
96+
/// DISCUSSION: We do not check that the types line up here so that we can
97+
/// allow for our users to transform our new value in ways that preserve
98+
/// ownership at \p oldValue before we perform the actual RAUW. If \p newValue
99+
/// is an object, any instructions in the chain of transforming instructions
100+
/// from \p newValue at \p oldValue's must be forwarding. If \p newValue is an
101+
/// address, then these transforms can only transform the address into a
102+
/// derived address.
103+
OwnershipRAUWHelper(OwnershipFixupContext &ctx,
104+
SingleValueInstruction *oldValue, SILValue newValue);
105+
106+
/// Returns true if this helper was initialized into a valid state.
107+
operator bool() const { return isValid(); }
108+
bool isValid() const { return bool(ctx) && bool(oldValue) && bool(newValue); }
109+
110+
/// Perform the actual RAUW. We require that \p newValue and \p oldValue have
111+
/// the same type at this point (in contrast to when calling
112+
/// OwnershipRAUWFixupHelper::get()).
113+
///
114+
/// This is so that we can avoid creating "forwarding" transformation
115+
/// instructions before we know if we can perform the RAUW. Any such
116+
/// "forwarding" transformation must be performed upon \p newValue at \p
117+
/// oldValue's insertion point so that we can then here RAUW the transformed
118+
/// \p newValue.
119+
SILBasicBlock::iterator perform();
120+
121+
private:
122+
SILBasicBlock::iterator replaceAddressUses(SingleValueInstruction *oldValue,
123+
SILValue newValue);
56124
};
57125

58126
} // namespace swift

lib/SIL/Utils/InstructionUtils.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,20 @@ SILValue swift::getUnderlyingObject(SILValue v) {
6262
}
6363
}
6464

65+
SILValue
66+
swift::getUnderlyingObjectStoppingAtObjectToAddrProjections(SILValue v) {
67+
if (!v->getType().isAddress())
68+
return SILValue();
69+
70+
while (true) {
71+
auto v2 = lookThroughAddressToAddressProjections(v);
72+
v2 = stripIndexingInsts(v2);
73+
if (v2 == v)
74+
return v2;
75+
v = v2;
76+
}
77+
}
78+
6579
SILValue swift::getUnderlyingObjectStopAtMarkDependence(SILValue v) {
6680
while (true) {
6781
SILValue v2 = stripCastsWithoutMarkDependence(v);
@@ -207,6 +221,15 @@ SILValue swift::stripAddressProjections(SILValue V) {
207221
}
208222
}
209223

224+
SILValue swift::lookThroughAddressToAddressProjections(SILValue v) {
225+
while (true) {
226+
v = stripSinglePredecessorArgs(v);
227+
if (!Projection::isAddressToAddressProjection(v))
228+
return v;
229+
v = cast<SingleValueInstruction>(v)->getOperand(0);
230+
}
231+
}
232+
210233
SILValue swift::stripValueProjections(SILValue V) {
211234
while (true) {
212235
V = stripSinglePredecessorArgs(V);

lib/SIL/Utils/OwnershipUtils.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -520,10 +520,9 @@ bool BorrowedValue::visitInteriorPointerOperands(
520520
// InteriorPointerOperand
521521
//===----------------------------------------------------------------------===//
522522

523-
bool InteriorPointerOperand::getImplicitUses(
524-
SmallVectorImpl<Operand *> &foundUses,
523+
bool InteriorPointerOperand::getImplicitUsesForAddress(
524+
SILValue projectedAddress, SmallVectorImpl<Operand *> &foundUses,
525525
std::function<void(Operand *)> *onError) {
526-
SILValue projectedAddress = getProjectedAddress();
527526
SmallVector<Operand *, 8> worklist(projectedAddress->getUses());
528527

529528
bool foundError = false;

lib/SILOptimizer/Analysis/SimplifyInstruction.cpp

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -300,14 +300,6 @@ SILValue InstSimplifier::visitAddressToPointerInst(AddressToPointerInst *ATPI) {
300300
}
301301

302302
SILValue InstSimplifier::visitPointerToAddressInst(PointerToAddressInst *PTAI) {
303-
// (pointer_to_address strict (address_to_pointer x)) -> x
304-
//
305-
// NOTE: We can not perform this optimization in OSSA without dealing with
306-
// interior pointers since we may be escaping an interior pointer address from
307-
// a borrow scope.
308-
if (PTAI->getFunction()->hasOwnership())
309-
return SILValue();
310-
311303
// If this address is not strict, then it cannot be replaced by an address
312304
// that may be strict.
313305
if (auto *ATPI = dyn_cast<AddressToPointerInst>(PTAI->getOperand()))
@@ -755,7 +747,8 @@ swift::replaceAllSimplifiedUsesAndErase(SILInstruction *i, SILValue result,
755747
if (svi->getFunction()->hasOwnership()) {
756748
JointPostDominanceSetComputer computer(*deadEndBlocks);
757749
OwnershipFixupContext ctx{callbacks, *deadEndBlocks, computer};
758-
return ctx.replaceAllUsesAndErase(svi, result);
750+
OwnershipRAUWHelper helper(ctx, svi, result);
751+
return helper.perform();
759752
}
760753
return replaceAllUsesAndErase(svi, result, callbacks);
761754
}
@@ -779,19 +772,39 @@ SILValue swift::simplifyOverflowBuiltinInstruction(BuiltinInst *BI) {
779772
///
780773
/// NOTE: We assume that the insertion point associated with the SILValue must
781774
/// dominate \p i.
782-
SILValue swift::simplifyInstruction(SILInstruction *i) {
783-
SILValue result = InstSimplifier().visit(i);
784-
if (!result)
785-
return SILValue();
786-
787-
// If we have a result, we know that we must have a single value instruction
788-
// by assumption since we have not implemented support in the rest of inst
789-
// simplify for non-single value instructions. We put the cast here so that
790-
// this code is not updated at this point in time.
791-
auto *svi = cast<SingleValueInstruction>(i);
792-
if (svi->getFunction()->hasOwnership())
793-
if (!OwnershipFixupContext::canFixUpOwnershipForRAUW(svi, result))
794-
return SILValue();
775+
static SILValue simplifyInstruction(SILInstruction *i) {
776+
return InstSimplifier().visit(i);
777+
}
795778

796-
return result;
779+
SILBasicBlock::iterator swift::simplifyAndReplaceAllSimplifiedUsesAndErase(
780+
SILInstruction *i, InstModCallbacks &callbacks,
781+
DeadEndBlocks *deadEndBlocks) {
782+
auto next = std::next(i->getIterator());
783+
auto *svi = dyn_cast<SingleValueInstruction>(i);
784+
if (!svi)
785+
return next;
786+
SILValue result = simplifyInstruction(i);
787+
788+
// If we fail to simplify or the simplified value returned is our passed in
789+
// value, just return std::next since we can't simplify.
790+
if (!result || svi == result)
791+
return next;
792+
793+
if (!svi->getFunction()->hasOwnership())
794+
return replaceAllUsesAndErase(svi, result, callbacks);
795+
796+
// If we weren't passed a dead end blocks, we can't optimize without ownership
797+
// enabled.
798+
if (!deadEndBlocks)
799+
return next;
800+
801+
JointPostDominanceSetComputer computer(*deadEndBlocks);
802+
OwnershipFixupContext ctx{callbacks, *deadEndBlocks, computer};
803+
OwnershipRAUWHelper helper(ctx, svi, result);
804+
805+
// If our RAUW helper is invalid, we do not support RAUWing this case, so
806+
// just return next.
807+
if (!helper.isValid())
808+
return next;
809+
return helper.perform();
797810
}

lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -479,13 +479,12 @@ static bool stripOwnership(SILFunction &func) {
479479
auto value = visitor.instructionsToSimplify.pop_back_val();
480480
if (!value.hasValue())
481481
continue;
482-
if (SILValue newValue = simplifyInstruction(*value)) {
483-
InstModCallbacks callbacks([&](SILInstruction *instToErase) {
484-
visitor.eraseInstruction(instToErase);
485-
});
486-
replaceAllSimplifiedUsesAndErase(*value, newValue, callbacks);
487-
madeChange = true;
488-
}
482+
InstModCallbacks callbacks([&](SILInstruction *instToErase) {
483+
visitor.eraseInstruction(instToErase);
484+
});
485+
// We are no longer in OSSA, so we don't need to pass in a deBlocks.
486+
simplifyAndReplaceAllSimplifiedUsesAndErase(*value, callbacks);
487+
madeChange |= callbacks.hadCallbackInvocation();
489488
}
490489

491490
return madeChange;

0 commit comments

Comments
 (0)