Skip to content

Commit 72a093d

Browse files
committed
[semantic-arc-opts] Teach the guaranteed copy_value peephole how to handle instructions that can forward either owned or guaranteed ownership.
This fixes issues exposed by my turning off the Nominal Type RValue peephole. It should give us some nice ARC wins as well potentially.
1 parent 384882d commit 72a093d

File tree

5 files changed

+279
-38
lines changed

5 files changed

+279
-38
lines changed

include/swift/SIL/SILInstruction.h

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,16 @@ class SILInstruction
505505
return isTypeDependentOperand(Op.getOperandNumber());
506506
}
507507

508+
private:
509+
/// Predicate used to filter OperandValueRange.
510+
struct OperandToValue;
511+
512+
public:
513+
using OperandValueRange =
514+
OptionalTransformRange<ArrayRef<Operand>, OperandToValue>;
515+
OperandValueRange
516+
getOperandValues(bool skipTypeDependentOperands = false) const;
517+
508518
SILValue getOperand(unsigned Num) const {
509519
return getAllOperands()[Num].get();
510520
}
@@ -514,6 +524,7 @@ class SILInstruction
514524
}
515525

516526
/// Return the list of results produced by this instruction.
527+
bool hasResults() const { return !getResults().empty(); }
517528
SILInstructionResultArray getResults() const { return getResultsImpl(); }
518529
unsigned getNumResults() const { return getResults().size(); }
519530

@@ -668,6 +679,27 @@ class SILInstruction
668679
static bool classof(const ValueBase *) = delete;
669680
};
670681

682+
struct SILInstruction::OperandToValue {
683+
const SILInstruction &i;
684+
bool skipTypeDependentOps;
685+
686+
OperandToValue(const SILInstruction &i, bool skipTypeDependentOps)
687+
: i(i), skipTypeDependentOps(skipTypeDependentOps) {}
688+
689+
Optional<SILValue> operator()(const Operand &use) const {
690+
if (skipTypeDependentOps && i.isTypeDependentOperand(use))
691+
return None;
692+
return use.get();
693+
}
694+
};
695+
696+
inline auto
697+
SILInstruction::getOperandValues(bool skipTypeDependentOperands) const
698+
-> OperandValueRange {
699+
return OperandValueRange(getAllOperands(),
700+
OperandToValue(*this, skipTypeDependentOperands));
701+
}
702+
671703
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
672704
const SILInstruction &I) {
673705
I.print(OS);
@@ -801,6 +833,9 @@ class OwnershipForwardingSingleValueInst : public SingleValueInstruction {
801833

802834
public:
803835
ValueOwnershipKind getOwnershipKind() const { return ownershipKind; }
836+
void setOwnershipKind(ValueOwnershipKind newOwnershipKind) {
837+
ownershipKind = newOwnershipKind;
838+
}
804839
};
805840

806841
/// A value base result of a multiple value instruction.
@@ -838,6 +873,11 @@ class MultipleValueInstructionResult : public ValueBase {
838873
/// This is stored in the bottom 3 bits of ValueBase's subclass data.
839874
ValueOwnershipKind getOwnershipKind() const;
840875

876+
/// Set the ownership kind assigned to this result.
877+
///
878+
/// This is stored in SILNode in the subclass data.
879+
void setOwnershipKind(ValueOwnershipKind Kind);
880+
841881
static bool classof(const SILInstruction *) = delete;
842882
static bool classof(const SILUndef *) = delete;
843883
static bool classof(const SILArgument *) = delete;
@@ -851,11 +891,6 @@ class MultipleValueInstructionResult : public ValueBase {
851891
}
852892

853893
protected:
854-
/// Set the ownership kind assigned to this result.
855-
///
856-
/// This is stored in SILNode in the subclass data.
857-
void setOwnershipKind(ValueOwnershipKind Kind);
858-
859894
/// Set the index of this result.
860895
void setIndex(unsigned NewIndex);
861896
};
@@ -4077,6 +4112,9 @@ class OwnershipForwardingConversionInst : public ConversionInst {
40774112

40784113
public:
40794114
ValueOwnershipKind getOwnershipKind() const { return ownershipKind; }
4115+
void setOwnershipKind(ValueOwnershipKind newOwnershipKind) {
4116+
ownershipKind = newOwnershipKind;
4117+
}
40804118
};
40814119

40824120
/// ConvertFunctionInst - Change the type of a function value without
@@ -5305,6 +5343,9 @@ class OwnershipForwardingSelectEnumInstBase : public SelectEnumInstBase {
53055343

53065344
public:
53075345
ValueOwnershipKind getOwnershipKind() const { return ownershipKind; }
5346+
void setOwnershipKind(ValueOwnershipKind newOwnershipKind) {
5347+
ownershipKind = newOwnershipKind;
5348+
}
53085349
};
53095350

53105351
/// Select one of a set of values based on the case of an enum.
@@ -7849,6 +7890,11 @@ inline void SILSuccessor::pred_iterator::cacheBasicBlock() {
78497890
}
78507891
}
78517892

7893+
// Declared in SILValue.h
7894+
inline bool Operand::isTypeDependent() const {
7895+
return getUser()->isTypeDependentOperand(*this);
7896+
}
7897+
78527898
} // end swift namespace
78537899

78547900
//===----------------------------------------------------------------------===//

include/swift/SIL/SILValue.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,11 @@ class Operand {
586586
SILInstruction *getUser() { return Owner; }
587587
const SILInstruction *getUser() const { return Owner; }
588588

589+
/// Return true if this operand is a type dependent operand.
590+
///
591+
/// Implemented in SILInstruction.h
592+
bool isTypeDependent() const;
593+
589594
/// Return which operand this is in the operand list of the using instruction.
590595
unsigned getOperandNumber() const;
591596

lib/SIL/OperandOwnership.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,10 @@ OperandOwnershipKindClassifier::visitFullApply(FullApplySite apply) {
719719
return Map::allLive();
720720
}
721721

722+
// If we have a type dependent operand, return an empty map.
723+
if (apply.getInstruction()->isTypeDependentOperand(op))
724+
return Map();
725+
722726
unsigned argIndex = apply.getCalleeArgIndex(op);
723727
auto conv = apply.getSubstCalleeConv();
724728
SILParameterInfo paramInfo = conv.getParamInfoForSILArg(argIndex);

lib/SILOptimizer/Mandatory/SemanticARCOpts.cpp

Lines changed: 107 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -40,36 +40,73 @@ STATISTIC(NumLoadCopyConvertedToLoadBorrow,
4040
///
4141
/// Semantically this implies that a value is never passed off as +1 to memory
4242
/// or another function implying it can be used everywhere at +0.
43-
static bool isConsumed(SILValue v,
44-
SmallVectorImpl<DestroyValueInst *> &destroys) {
43+
static bool isConsumed(
44+
SILValue v, SmallVectorImpl<DestroyValueInst *> &destroys,
45+
NullablePtr<SmallVectorImpl<SILInstruction *>> forwardingInsts = nullptr) {
4546
assert(v.getOwnershipKind() == ValueOwnershipKind::Owned);
46-
return !all_of(v->getUses(), [&destroys](Operand *op) {
47-
// We know that a copy_value produces an @owned value. Look
48-
// through all of our uses and classify them as either
49-
// invalidating or not invalidating. Make sure that all of the
50-
// invalidating ones are destroy_value since otherwise the
51-
// live_range is not complete.
47+
SmallVector<Operand *, 32> worklist(v->use_begin(), v->use_end());
48+
while (!worklist.empty()) {
49+
auto *op = worklist.pop_back_val();
50+
51+
// Skip type dependent operands.
52+
if (op->isTypeDependent())
53+
continue;
54+
55+
auto *user = op->getUser();
56+
57+
// We know that a copy_value produces an @owned value. Look through all of
58+
// our uses and classify them as either invalidating or not
59+
// invalidating. Make sure that all of the invalidating ones are
60+
// destroy_value since otherwise the live_range is not complete.
5261
auto map = op->getOwnershipKindMap();
5362
auto constraint = map.getLifetimeConstraint(ValueOwnershipKind::Owned);
5463
switch (constraint) {
5564
case UseLifetimeConstraint::MustBeInvalidated: {
5665
// See if we have a destroy value. If we don't we have an
5766
// unknown consumer. Return false, we need this live range.
58-
auto *dvi = dyn_cast<DestroyValueInst>(op->getUser());
59-
if (!dvi)
60-
return false;
67+
if (auto *dvi = dyn_cast<DestroyValueInst>(user)) {
68+
destroys.push_back(dvi);
69+
continue;
70+
}
71+
72+
// Otherwise, see if we have a forwarding value that has a single
73+
// non-trivial operand that can accept a guaranteed value. If so, at its
74+
// users to the worklist and continue.
75+
//
76+
// DISCUSSION: For now we do not support forwarding instructions with
77+
// multiple non-trivial arguments since we would need to optimize all of
78+
// the non-trivial arguments at the same time.
79+
//
80+
// NOTE: Today we do not support TermInsts for simplicity... we /could/
81+
// support it though if we need to.
82+
if (forwardingInsts.isNonNull() && !isa<TermInst>(user) &&
83+
isGuaranteedForwardingInst(user) &&
84+
1 == count_if(user->getOperandValues(
85+
true /*ignore type dependent operands*/),
86+
[&](SILValue v) {
87+
return v.getOwnershipKind() ==
88+
ValueOwnershipKind::Owned;
89+
})) {
90+
forwardingInsts.get()->push_back(user);
91+
for (SILValue v : user->getResults()) {
92+
copy(v->getUses(), std::back_inserter(worklist));
93+
}
94+
continue;
95+
}
6196

62-
// Otherwise, return true and stash this destroy value.
63-
destroys.push_back(dvi);
97+
// Otherwise be conservative and assume that we /may consume/ the value.
6498
return true;
6599
}
66100
case UseLifetimeConstraint::MustBeLive:
67101
// Ok, this constraint can take something owned as live. Lets
68102
// see if it can also take something that is guaranteed. If it
69103
// can not, then we bail.
70-
return map.canAcceptKind(ValueOwnershipKind::Guaranteed);
104+
map.canAcceptKind(ValueOwnershipKind::Guaranteed);
105+
continue;
71106
}
72-
});
107+
}
108+
109+
return false;
73110
}
74111

75112
//===----------------------------------------------------------------------===//
@@ -168,13 +205,14 @@ static bool performGuaranteedCopyValueOptimization(CopyValueInst *cvi) {
168205
if (!canHandleOperand(cvi->getOperand(), borrowIntroducers))
169206
return false;
170207

171-
// Then go over all of our uses. Find our destroying instructions
172-
// and make sure all of them are destroy_value. For our
173-
// non-destroying instructions, make sure that they accept a
174-
// guaranteed value. After that, make sure that our destroys are
175-
// within the lifetime of our borrowed values.
208+
// Then go over all of our uses. Find our destroying instructions (ignoring
209+
// forwarding instructions that can forward both owned and guaranteed) and
210+
// make sure all of them are destroy_value. For our non-destroying
211+
// instructions, make sure that they accept a guaranteed value. After that,
212+
// make sure that our destroys are within the lifetime of our borrowed values.
176213
SmallVector<DestroyValueInst *, 16> destroys;
177-
if (isConsumed(cvi, destroys))
214+
SmallVector<SILInstruction *, 16> guaranteedForwardingInsts;
215+
if (isConsumed(cvi, destroys, &guaranteedForwardingInsts))
178216
return false;
179217

180218
// If we reached this point, then we know that all of our users can
@@ -189,15 +227,56 @@ static bool performGuaranteedCopyValueOptimization(CopyValueInst *cvi) {
189227
assert(all_of(borrowIntroducers,
190228
[](SILValue v) { return isa<SILFunctionArgument>(v); }));
191229

192-
// Otherwise, we know that our copy_value/destroy_values are all
193-
// completely within the guaranteed value scope.
230+
// Otherwise, we know that our copy_value/destroy_values are all completely
231+
// within the guaranteed value scope. First delete the destroys/copies.
194232
while (!destroys.empty()) {
195233
auto *dvi = destroys.pop_back_val();
196234
dvi->eraseFromParent();
197235
++NumEliminatedInsts;
198236
}
199237
cvi->replaceAllUsesWith(cvi->getOperand());
200238
cvi->eraseFromParent();
239+
240+
// Then change all of our guaranteed forwarding insts to have guaranteed
241+
// ownership kind instead of what ever they previously had (ignoring trivial
242+
// results);
243+
while (!guaranteedForwardingInsts.empty()) {
244+
auto *i = guaranteedForwardingInsts.pop_back_val();
245+
246+
assert(i->hasResults());
247+
248+
for (SILValue result : i->getResults()) {
249+
if (auto *svi = dyn_cast<OwnershipForwardingSingleValueInst>(result)) {
250+
if (svi->getOwnershipKind() == ValueOwnershipKind::Owned) {
251+
svi->setOwnershipKind(ValueOwnershipKind::Guaranteed);
252+
}
253+
continue;
254+
}
255+
256+
if (auto *ofci = dyn_cast<OwnershipForwardingConversionInst>(result)) {
257+
if (ofci->getOwnershipKind() == ValueOwnershipKind::Owned) {
258+
ofci->setOwnershipKind(ValueOwnershipKind::Guaranteed);
259+
}
260+
continue;
261+
}
262+
263+
if (auto *sei = dyn_cast<OwnershipForwardingSelectEnumInstBase>(result)) {
264+
if (sei->getOwnershipKind() == ValueOwnershipKind::Owned) {
265+
sei->setOwnershipKind(ValueOwnershipKind::Guaranteed);
266+
}
267+
continue;
268+
}
269+
270+
if (auto *mvir = dyn_cast<MultipleValueInstructionResult>(result)) {
271+
if (mvir->getOwnershipKind() == ValueOwnershipKind::Owned) {
272+
mvir->setOwnershipKind(ValueOwnershipKind::Guaranteed);
273+
}
274+
continue;
275+
}
276+
277+
llvm_unreachable("unhandled forwarding instruction?!");
278+
}
279+
}
201280
++NumEliminatedInsts;
202281
return true;
203282
}
@@ -350,6 +429,11 @@ bool SemanticARCOptVisitor::visitLoadInst(LoadInst *li) {
350429
// load_borrow.
351430
auto *lbi =
352431
SILBuilderWithScope(li).createLoadBorrow(li->getLoc(), li->getOperand());
432+
433+
// Since we are looking through forwarding uses that can accept guaranteed
434+
// parameters, we can have multiple destroy_value along the same path. We need
435+
// to find the post-dominating block set of these destroy value to ensure that
436+
// we do not insert multiple end_borrow.
353437
while (!destroyValues.empty()) {
354438
auto *dvi = destroyValues.pop_back_val();
355439
SILBuilderWithScope(dvi).createEndBorrow(dvi->getLoc(), lbi);

0 commit comments

Comments
 (0)