Skip to content

Commit 36563da

Browse files
authored
Merge pull request swiftlang#23470 from gottesmm/pr-4caf2d670d87f321e8b8c9d44ccb8814d3e27f85
2 parents bfc2753 + 72a093d commit 36563da

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)