Skip to content

Commit 73ba521

Browse files
committed
[ownership] Add a new API to OwnershipFixupContext::replaceAllAddressUsesFixingInteriorPointerOwnership.
In OSSA, we enforce that addresses from interior pointer instructions are scoped within a borrow scope. This means that it is invalid to use such an address outside of its parent borrow scope and as a result one can not just RAUW an address value by a dominating address value since the latter may be invalid at the former. I foresee that I am going to have to solve this problem and so I decided to write this API to handle the vast majority of cases. The way this API works is that it: 1. Computes an access path with base for the new value. If we do not have a base value and a valid access path with root, we bail. 2. Then we check if our base value is the result of an interior pointer instruction. If it isn't, we are immediately done and can RAUW without further delay. 3. If we do have an interior pointer instruction, we see if the immediate guaranteed value we projected from has a single borrow introducer value. If not, we bail. I think this is reasonable since with time, all guaranteed values will always only have a single borrow introducing value (once struct, tuple, destructure_struct, destructure_tuple become reborrows). 4. Then we gather up all inner uses of our access path. If for some reason that fails, we bail. 5. Then we see if all of those uses are within our borrow scope. If so, we can RAUW without any further worry. 6. Otherwise, we perform a copy+borrow of our interior pointer's operand value at the interior pointer, create a copy of the interior pointer instruction upon this new borrow and then RAUW oldValue with that instead. By construction all uses of oldValue will be within this new interior pointer scope.
1 parent fe4c345 commit 73ba521

File tree

7 files changed

+390
-110
lines changed

7 files changed

+390
-110
lines changed

include/swift/SIL/OwnershipUtils.h

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

674684
Operand *operator->() { return operand; }
675685
const Operand *operator->() const { return operand; }

include/swift/SILOptimizer/Utils/OwnershipOptUtils.h

Lines changed: 67 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,39 +38,89 @@ struct OwnershipFixupContext {
3838
DeadEndBlocks &deBlocks;
3939
JointPostDominanceSetComputer &jointPostDomSetComputer;
4040

41-
OwnershipFixupContext(InstModCallbacks &callbacks, DeadEndBlocks &deBlocks,
42-
JointPostDominanceSetComputer &inputJPDComputer)
43-
: inlineCallbacks(), callbacks(callbacks), deBlocks(deBlocks),
44-
jointPostDomSetComputer(inputJPDComputer) {}
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;
4555

46-
OwnershipFixupContext(DeadEndBlocks &deBlocks,
47-
JointPostDominanceSetComputer &inputJPDComputer)
48-
: inlineCallbacks(InstModCallbacks()), callbacks(*inlineCallbacks),
49-
deBlocks(deBlocks), jointPostDomSetComputer(inputJPDComputer) {}
56+
void clear() {
57+
allAddressUsesFromOldValue.clear();
58+
intPtrOp = InteriorPointerOperand();
59+
}
60+
};
61+
AddressFixupContext extraAddressFixupInfo;
62+
63+
OwnershipFixupContext(InstModCallbacks &callbacks, DeadEndBlocks &deBlocks,
64+
JointPostDominanceSetComputer &jointPostDomSetComputer)
65+
: callbacks(callbacks), deBlocks(deBlocks),
66+
jointPostDomSetComputer(jointPostDomSetComputer) {}
5067

5168
void clear() {
5269
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);
5379
}
5480
};
5581

5682
/// A utility composed ontop of OwnershipFixupContext that knows how to RAUW a
5783
/// value or a single value instruction with a new value and then fixup
5884
/// ownership invariants afterwards.
5985
class OwnershipRAUWHelper {
60-
OwnershipFixupContext &ctx;
86+
OwnershipFixupContext *ctx;
87+
SingleValueInstruction *oldValue;
88+
SILValue newValue;
6189

6290
public:
63-
OwnershipRAUWHelper(OwnershipFixupContext &ctx) : ctx(ctx) {}
91+
OwnershipRAUWHelper() : ctx(nullptr), oldValue(nullptr), newValue(nullptr) {}
92+
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.
95+
///
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);
64105

65-
SILBasicBlock::iterator
66-
replaceAllUsesAndErase(SingleValueInstruction *oldValue, SILValue newValue);
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); }
67109

68-
/// We can not RAUW all old values with new values.
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()).
69113
///
70-
/// Namely, we do not support RAUWing values with ValueOwnershipKind::None
71-
/// that have uses that do not require ValueOwnershipKind::None or
72-
/// ValueOwnershipKind::Any.
73-
static bool canFixUpOwnershipForRAUW(SILValue oldValue, SILValue newValue);
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);
74124
};
75125

76126
} // namespace swift

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: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -755,8 +755,8 @@ swift::replaceAllSimplifiedUsesAndErase(SILInstruction *i, SILValue result,
755755
if (svi->getFunction()->hasOwnership()) {
756756
JointPostDominanceSetComputer computer(*deadEndBlocks);
757757
OwnershipFixupContext ctx{callbacks, *deadEndBlocks, computer};
758-
OwnershipRAUWHelper helper(ctx);
759-
return helper.replaceAllUsesAndErase(svi, result);
758+
OwnershipRAUWHelper helper(ctx, svi, result);
759+
return helper.perform();
760760
}
761761
return replaceAllUsesAndErase(svi, result, callbacks);
762762
}
@@ -781,20 +781,7 @@ SILValue swift::simplifyOverflowBuiltinInstruction(BuiltinInst *BI) {
781781
/// NOTE: We assume that the insertion point associated with the SILValue must
782782
/// dominate \p i.
783783
static SILValue simplifyInstruction(SILInstruction *i) {
784-
SILValue result = InstSimplifier().visit(i);
785-
if (!result)
786-
return SILValue();
787-
788-
// If we have a result, we know that we must have a single value instruction
789-
// by assumption since we have not implemented support in the rest of inst
790-
// simplify for non-single value instructions. We put the cast here so that
791-
// this code is not updated at this point in time.
792-
auto *svi = cast<SingleValueInstruction>(i);
793-
if (svi->getFunction()->hasOwnership())
794-
if (!OwnershipRAUWHelper::canFixUpOwnershipForRAUW(svi, result))
795-
return SILValue();
796-
797-
return result;
784+
return InstSimplifier().visit(i);
798785
}
799786

800787
SILBasicBlock::iterator swift::simplifyAndReplaceAllSimplifiedUsesAndErase(
@@ -811,11 +798,16 @@ SILBasicBlock::iterator swift::simplifyAndReplaceAllSimplifiedUsesAndErase(
811798
if (!result || svi == result)
812799
return next;
813800

814-
if (svi->getFunction()->hasOwnership()) {
815-
JointPostDominanceSetComputer computer(*deadEndBlocks);
816-
OwnershipFixupContext ctx{callbacks, *deadEndBlocks, computer};
817-
OwnershipRAUWHelper helper(ctx);
818-
return helper.replaceAllUsesAndErase(svi, result);
819-
}
820-
return replaceAllUsesAndErase(svi, result, callbacks);
801+
if (!svi->getFunction()->hasOwnership())
802+
return replaceAllUsesAndErase(svi, result, callbacks);
803+
804+
JointPostDominanceSetComputer computer(*deadEndBlocks);
805+
OwnershipFixupContext ctx{callbacks, *deadEndBlocks, computer};
806+
OwnershipRAUWHelper helper(ctx, svi, result);
807+
808+
// If our RAUW helper is invalid, we do not support RAUWing this case, so
809+
// just return next.
810+
if (!helper.isValid())
811+
return next;
812+
return helper.perform();
821813
}

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,8 @@ class SILCombiner :
9696
/// post-dominating blocks to a full jointly post-dominating set.
9797
JointPostDominanceSetComputer jPostDomComputer;
9898

99-
/// A utility that we use to perform erase+RAUW that fixes up ownership for us
100-
/// afterwards by lifetime extending/copy values as appropriate. We rely on
101-
/// later optimizations to chew through this traffic. This ensures we can use
102-
/// one code base for both OSSA and non-OSSA.
103-
OwnershipFixupContext ownershipRAUWHelper;
99+
/// External context struct used by \see ownershipRAUWHelper.
100+
OwnershipFixupContext ownershipFixupContext;
104101

105102
public:
106103
SILCombiner(SILOptFunctionBuilder &FuncBuilder, SILBuilder &B,
@@ -134,7 +131,7 @@ class SILCombiner :
134131
Worklist.add(use->getUser());
135132
}),
136133
deBlocks(&B.getFunction()), jPostDomComputer(deBlocks),
137-
ownershipRAUWHelper(instModCallbacks, deBlocks, jPostDomComputer) {}
134+
ownershipFixupContext(instModCallbacks, deBlocks, jPostDomComputer) {}
138135

139136
bool runOnFunction(SILFunction &F);
140137

lib/SILOptimizer/Transforms/CSE.cpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -583,17 +583,18 @@ class CSE {
583583

584584
DeadEndBlocks &DeadEndBBs;
585585

586-
OwnershipRAUWHelper &RAUWHelper;
586+
OwnershipFixupContext &RAUWFixupContext;
587587

588588
/// The set of calls to lazy property getters which can be replace by a direct
589589
/// load of the property value.
590590
llvm::SmallVector<ApplyInst *, 8> lazyPropertyGetters;
591591

592592
CSE(bool RunsOnHighLevelSil, SideEffectAnalysis *SEA,
593593
SILOptFunctionBuilder &FuncBuilder, DeadEndBlocks &DeadEndBBs,
594-
OwnershipRAUWHelper &RAUWHelper)
594+
OwnershipFixupContext &RAUWFixupContext)
595595
: SEA(SEA), FuncBuilder(FuncBuilder), DeadEndBBs(DeadEndBBs),
596-
RAUWHelper(RAUWHelper), RunsOnHighLevelSil(RunsOnHighLevelSil) {}
596+
RAUWFixupContext(RAUWFixupContext),
597+
RunsOnHighLevelSil(RunsOnHighLevelSil) {}
597598

598599
bool processFunction(SILFunction &F, DominanceInfo *DT);
599600

@@ -1017,14 +1018,14 @@ bool CSE::processNode(DominanceInfoNode *Node) {
10171018
// extend it here as well
10181019
if (!isa<SingleValueInstruction>(Inst))
10191020
continue;
1020-
if (!OwnershipRAUWHelper::canFixUpOwnershipForRAUW(
1021-
cast<SingleValueInstruction>(Inst),
1022-
cast<SingleValueInstruction>(AvailInst)))
1021+
1022+
OwnershipRAUWHelper helper(RAUWFixupContext,
1023+
cast<SingleValueInstruction>(Inst),
1024+
cast<SingleValueInstruction>(AvailInst));
1025+
if (!helper.isValid())
10231026
continue;
10241027
// Replace SingleValueInstruction using OSSA RAUW here
1025-
nextI = RAUWHelper.replaceAllUsesAndErase(
1026-
cast<SingleValueInstruction>(Inst),
1027-
cast<SingleValueInstruction>(AvailInst));
1028+
nextI = helper.perform();
10281029
Changed = true;
10291030
++NumCSE;
10301031
continue;
@@ -1399,8 +1400,7 @@ class SILCSE : public SILFunctionTransform {
13991400
JointPostDominanceSetComputer Computer(DeadEndBBs);
14001401
InstModCallbacks callbacks;
14011402
OwnershipFixupContext FixupCtx{callbacks, DeadEndBBs, Computer};
1402-
OwnershipRAUWHelper RAUWHelper(FixupCtx);
1403-
CSE C(RunsOnHighLevelSil, SEA, FuncBuilder, DeadEndBBs, RAUWHelper);
1403+
CSE C(RunsOnHighLevelSil, SEA, FuncBuilder, DeadEndBBs, FixupCtx);
14041404
bool Changed = false;
14051405

14061406
// Perform the traditional CSE.

0 commit comments

Comments
 (0)