Skip to content

Commit adba0f0

Browse files
authored
Merge pull request swiftlang#33702 from gottesmm/pr-03d4e156c1dfdff0133077eb67a79e955dd813c8
2 parents 6f9df09 + 364b7c7 commit adba0f0

File tree

5 files changed

+695
-646
lines changed

5 files changed

+695
-646
lines changed
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
target_sources(swiftSILOptimizer PRIVATE
2-
SemanticARCOpts.cpp)
2+
SemanticARCOpts.cpp
3+
OwnershipLiveRange.cpp)
Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
//===--- OwnershipLiveRange.cpp -------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "OwnershipLiveRange.h"
14+
#include "OwnershipPhiOperand.h"
15+
16+
using namespace swift;
17+
using namespace swift::semanticarc;
18+
19+
OwnershipLiveRange::OwnershipLiveRange(SILValue value)
20+
: introducer(*OwnedValueIntroducer::get(value)), destroyingUses(),
21+
ownershipForwardingUses(), unknownConsumingUses() {
22+
assert(introducer.value.getOwnershipKind() == ValueOwnershipKind::Owned);
23+
24+
SmallVector<Operand *, 32> tmpDestroyingUses;
25+
SmallVector<Operand *, 32> tmpForwardingConsumingUses;
26+
SmallVector<Operand *, 32> tmpUnknownConsumingUses;
27+
28+
// We know that our silvalue produces an @owned value. Look through all of our
29+
// uses and classify them as either consuming or not.
30+
SmallVector<Operand *, 32> worklist(introducer.value->getUses());
31+
while (!worklist.empty()) {
32+
auto *op = worklist.pop_back_val();
33+
34+
// Skip type dependent operands.
35+
if (op->isTypeDependent())
36+
continue;
37+
38+
// Do a quick check that we did not add ValueOwnershipKind that are not
39+
// owned to the worklist.
40+
assert(op->get().getOwnershipKind() == ValueOwnershipKind::Owned &&
41+
"Added non-owned value to worklist?!");
42+
43+
auto *user = op->getUser();
44+
45+
// Ok, this constraint can take something owned as live. Assert that it
46+
// can also accept something that is guaranteed. Any non-consuming use of
47+
// an owned value should be able to take a guaranteed parameter as well
48+
// (modulo bugs). We assert to catch these.
49+
if (!op->isConsumingUse()) {
50+
continue;
51+
}
52+
53+
// Ok, we know now that we have a consuming use. See if we have a destroy
54+
// value, quickly up front. If we do have one, stash it and continue.
55+
if (isa<DestroyValueInst>(user)) {
56+
tmpDestroyingUses.push_back(op);
57+
continue;
58+
}
59+
60+
// Otherwise, see if we have a forwarding value that has a single
61+
// non-trivial operand that can accept a guaranteed value. If not, we can
62+
// not recursively process it, so be conservative and assume that we /may
63+
// consume/ the value, so the live range must not be eliminated.
64+
//
65+
// DISCUSSION: For now we do not support forwarding instructions with
66+
// multiple non-trivial arguments since we would need to optimize all of
67+
// the non-trivial arguments at the same time.
68+
//
69+
// NOTE: Today we do not support TermInsts for simplicity... we /could/
70+
// support it though if we need to.
71+
auto *ti = dyn_cast<TermInst>(user);
72+
if ((ti && !ti->isTransformationTerminator()) ||
73+
!isGuaranteedForwardingInst(user) ||
74+
1 != count_if(user->getOperandValues(
75+
true /*ignore type dependent operands*/),
76+
[&](SILValue v) {
77+
return v.getOwnershipKind() ==
78+
ValueOwnershipKind::Owned;
79+
})) {
80+
tmpUnknownConsumingUses.push_back(op);
81+
continue;
82+
}
83+
84+
// Ok, this is a forwarding instruction whose ownership we can flip from
85+
// owned -> guaranteed.
86+
tmpForwardingConsumingUses.push_back(op);
87+
88+
// If we have a non-terminator, just visit its users recursively to see if
89+
// the the users force the live range to be alive.
90+
if (!ti) {
91+
for (SILValue v : user->getResults()) {
92+
if (v.getOwnershipKind() != ValueOwnershipKind::Owned)
93+
continue;
94+
llvm::copy(v->getUses(), std::back_inserter(worklist));
95+
}
96+
continue;
97+
}
98+
99+
// Otherwise, we know that we have no only a terminator, but a
100+
// transformation terminator, so we should add the users of its results to
101+
// the worklist.
102+
for (auto &succ : ti->getSuccessors()) {
103+
auto *succBlock = succ.getBB();
104+
105+
// If we do not have any arguments, then continue.
106+
if (succBlock->args_empty())
107+
continue;
108+
109+
for (auto *succArg : succBlock->getSILPhiArguments()) {
110+
// If we have an any value, just continue.
111+
if (succArg->getOwnershipKind() == ValueOwnershipKind::None)
112+
continue;
113+
114+
// Otherwise add all users of this BBArg to the worklist to visit
115+
// recursively.
116+
llvm::copy(succArg->getUses(), std::back_inserter(worklist));
117+
}
118+
}
119+
}
120+
121+
// The order in which we append these to consumingUses matters since we assume
122+
// their order as an invariant. This is done to ensure that we can pass off
123+
// all of our uses or individual sub-arrays of our users without needing to
124+
// move around memory.
125+
llvm::copy(tmpDestroyingUses, std::back_inserter(consumingUses));
126+
llvm::copy(tmpForwardingConsumingUses, std::back_inserter(consumingUses));
127+
llvm::copy(tmpUnknownConsumingUses, std::back_inserter(consumingUses));
128+
129+
auto cUseArrayRef = llvm::makeArrayRef(consumingUses);
130+
destroyingUses = cUseArrayRef.take_front(tmpDestroyingUses.size());
131+
ownershipForwardingUses = cUseArrayRef.slice(
132+
tmpDestroyingUses.size(), tmpForwardingConsumingUses.size());
133+
unknownConsumingUses = cUseArrayRef.take_back(tmpUnknownConsumingUses.size());
134+
}
135+
136+
void OwnershipLiveRange::insertEndBorrowsAtDestroys(
137+
SILValue newGuaranteedValue, DeadEndBlocks &deadEndBlocks,
138+
ValueLifetimeAnalysis::Frontier &scratch) {
139+
assert(scratch.empty() && "Expected scratch to be initially empty?!");
140+
141+
// Since we are looking through forwarding uses that can accept guaranteed
142+
// parameters, we can have multiple destroy_value along the same path. We need
143+
// to find the post-dominating block set of these destroy value to ensure that
144+
// we do not insert multiple end_borrow.
145+
//
146+
// TODO: Hoist this out?
147+
SILInstruction *inst = introducer.value->getDefiningInstruction();
148+
Optional<ValueLifetimeAnalysis> analysis;
149+
if (!inst) {
150+
analysis.emplace(cast<SILArgument>(introducer.value),
151+
getAllConsumingInsts());
152+
} else {
153+
analysis.emplace(inst, getAllConsumingInsts());
154+
}
155+
156+
// Use all consuming uses in our value lifetime analysis to ensure correctness
157+
// in the face of unreachable code.
158+
bool foundCriticalEdges = !analysis->computeFrontier(
159+
scratch, ValueLifetimeAnalysis::DontModifyCFG, &deadEndBlocks);
160+
(void)foundCriticalEdges;
161+
assert(!foundCriticalEdges);
162+
auto loc = RegularLocation::getAutoGeneratedLocation();
163+
while (!scratch.empty()) {
164+
auto *insertPoint = scratch.pop_back_val();
165+
SILBuilderWithScope builder(insertPoint);
166+
builder.createEndBorrow(loc, newGuaranteedValue);
167+
}
168+
}
169+
170+
static void convertInstructionOwnership(SILInstruction *i,
171+
ValueOwnershipKind oldOwnership,
172+
ValueOwnershipKind newOwnership) {
173+
// If this is a term inst, just convert all of its incoming values that are
174+
// owned to be guaranteed.
175+
if (auto *ti = dyn_cast<TermInst>(i)) {
176+
for (auto &succ : ti->getSuccessors()) {
177+
auto *succBlock = succ.getBB();
178+
179+
// If we do not have any arguments, then continue.
180+
if (succBlock->args_empty())
181+
continue;
182+
183+
for (auto *succArg : succBlock->getSILPhiArguments()) {
184+
// If we have an any value, just continue.
185+
if (succArg->getOwnershipKind() == oldOwnership) {
186+
succArg->setOwnershipKind(newOwnership);
187+
}
188+
}
189+
}
190+
return;
191+
}
192+
193+
assert(i->hasResults());
194+
for (SILValue result : i->getResults()) {
195+
if (auto *svi = dyn_cast<OwnershipForwardingSingleValueInst>(result)) {
196+
if (svi->getOwnershipKind() == oldOwnership) {
197+
svi->setOwnershipKind(newOwnership);
198+
}
199+
continue;
200+
}
201+
202+
if (auto *ofci = dyn_cast<OwnershipForwardingConversionInst>(result)) {
203+
if (ofci->getOwnershipKind() == oldOwnership) {
204+
ofci->setOwnershipKind(newOwnership);
205+
}
206+
continue;
207+
}
208+
209+
if (auto *sei = dyn_cast<OwnershipForwardingSelectEnumInstBase>(result)) {
210+
if (sei->getOwnershipKind() == oldOwnership) {
211+
sei->setOwnershipKind(newOwnership);
212+
}
213+
continue;
214+
}
215+
216+
if (auto *mvir = dyn_cast<MultipleValueInstructionResult>(result)) {
217+
if (mvir->getOwnershipKind() == oldOwnership) {
218+
mvir->setOwnershipKind(newOwnership);
219+
}
220+
continue;
221+
}
222+
223+
llvm_unreachable("unhandled forwarding instruction?!");
224+
}
225+
}
226+
227+
void OwnershipLiveRange::convertOwnedGeneralForwardingUsesToGuaranteed() && {
228+
while (!ownershipForwardingUses.empty()) {
229+
auto *i = ownershipForwardingUses.back()->getUser();
230+
ownershipForwardingUses = ownershipForwardingUses.drop_back();
231+
convertInstructionOwnership(i, ValueOwnershipKind::Owned,
232+
ValueOwnershipKind::Guaranteed);
233+
}
234+
}
235+
236+
void OwnershipLiveRange::convertToGuaranteedAndRAUW(
237+
SILValue newGuaranteedValue, InstModCallbacks callbacks) && {
238+
auto *value = cast<SingleValueInstruction>(introducer.value);
239+
while (!destroyingUses.empty()) {
240+
auto *d = destroyingUses.back();
241+
destroyingUses = destroyingUses.drop_back();
242+
callbacks.deleteInst(d->getUser());
243+
}
244+
245+
callbacks.eraseAndRAUWSingleValueInst(value, newGuaranteedValue);
246+
247+
// Then change all of our guaranteed forwarding insts to have guaranteed
248+
// ownership kind instead of what ever they previously had (ignoring trivial
249+
// results);
250+
std::move(*this).convertOwnedGeneralForwardingUsesToGuaranteed();
251+
}
252+
253+
// TODO: If this is useful, move onto OwnedValueIntroducer itself?
254+
static SILValue convertIntroducerToGuaranteed(OwnedValueIntroducer introducer) {
255+
switch (introducer.kind) {
256+
case OwnedValueIntroducerKind::Phi: {
257+
auto *phiArg = cast<SILPhiArgument>(introducer.value);
258+
phiArg->setOwnershipKind(ValueOwnershipKind::Guaranteed);
259+
return phiArg;
260+
}
261+
case OwnedValueIntroducerKind::Struct: {
262+
auto *si = cast<StructInst>(introducer.value);
263+
si->setOwnershipKind(ValueOwnershipKind::Guaranteed);
264+
return si;
265+
}
266+
case OwnedValueIntroducerKind::Tuple: {
267+
auto *ti = cast<TupleInst>(introducer.value);
268+
ti->setOwnershipKind(ValueOwnershipKind::Guaranteed);
269+
return ti;
270+
}
271+
case OwnedValueIntroducerKind::Copy:
272+
case OwnedValueIntroducerKind::LoadCopy:
273+
case OwnedValueIntroducerKind::Apply:
274+
case OwnedValueIntroducerKind::BeginApply:
275+
case OwnedValueIntroducerKind::TryApply:
276+
case OwnedValueIntroducerKind::LoadTake:
277+
case OwnedValueIntroducerKind::FunctionArgument:
278+
case OwnedValueIntroducerKind::PartialApplyInit:
279+
case OwnedValueIntroducerKind::AllocBoxInit:
280+
case OwnedValueIntroducerKind::AllocRefInit:
281+
return SILValue();
282+
}
283+
}
284+
285+
void OwnershipLiveRange::convertJoinedLiveRangePhiToGuaranteed(
286+
DeadEndBlocks &deadEndBlocks, ValueLifetimeAnalysis::Frontier &scratch,
287+
InstModCallbacks callbacks) && {
288+
289+
// First convert the phi value itself to be guaranteed.
290+
SILValue phiValue = convertIntroducerToGuaranteed(introducer);
291+
292+
// Then insert end_borrows at each of our destroys if we are consuming. We
293+
// have to convert the phi to guaranteed first since otherwise, the ownership
294+
// check when we create the end_borrows will trigger.
295+
if (introducer.hasConsumingGuaranteedOperands()) {
296+
insertEndBorrowsAtDestroys(phiValue, deadEndBlocks, scratch);
297+
}
298+
299+
// Then eliminate all of the destroys...
300+
while (!destroyingUses.empty()) {
301+
auto *d = destroyingUses.back();
302+
destroyingUses = destroyingUses.drop_back();
303+
callbacks.deleteInst(d->getUser());
304+
}
305+
306+
// and change all of our guaranteed forwarding insts to have guaranteed
307+
// ownership kind instead of what ever they previously had (ignoring trivial
308+
// results);
309+
std::move(*this).convertOwnedGeneralForwardingUsesToGuaranteed();
310+
}
311+
312+
OwnershipLiveRange::HasConsumingUse_t
313+
OwnershipLiveRange::hasUnknownConsumingUse(bool assumingAtFixPoint) const {
314+
// First do a quick check if we have /any/ unknown consuming
315+
// uses. If we do not have any, return false early.
316+
if (unknownConsumingUses.empty()) {
317+
return HasConsumingUse_t::No;
318+
}
319+
320+
// Ok, we do have some unknown consuming uses. If we aren't assuming we are at
321+
// the fixed point yet, just bail.
322+
if (!assumingAtFixPoint) {
323+
return HasConsumingUse_t::Yes;
324+
}
325+
326+
// We do not know how to handle yet cases where an owned value is used by
327+
// multiple phi nodes. So we bail early if unknown consuming uses is > 1.
328+
//
329+
// TODO: Build up phi node web.
330+
auto *op = getSingleUnknownConsumingUse();
331+
if (!op) {
332+
return HasConsumingUse_t::Yes;
333+
}
334+
335+
// Make sure our single unknown consuming use is a branch inst. If not, bail,
336+
// this is a /real/ unknown consuming use.
337+
if (!OwnershipPhiOperand::get(op)) {
338+
return HasConsumingUse_t::Yes;
339+
}
340+
341+
// Otherwise, setup the phi to incoming value map mapping the block arguments
342+
// to our introducer.
343+
return HasConsumingUse_t::YesButAllPhiArgs;
344+
}
345+
346+
OwnershipLiveRange::DestroyingInstsRange
347+
OwnershipLiveRange::getDestroyingInsts() const {
348+
return DestroyingInstsRange(getDestroyingUses(), OperandToUser());
349+
}
350+
351+
OwnershipLiveRange::ConsumingInstsRange
352+
OwnershipLiveRange::getAllConsumingInsts() const {
353+
return ConsumingInstsRange(consumingUses, OperandToUser());
354+
}

0 commit comments

Comments
 (0)