-
Notifications
You must be signed in to change notification settings - Fork 10.4k
/
Copy pathOwnedToGuaranteedPhiOpt.cpp
274 lines (243 loc) · 11.3 KB
/
OwnedToGuaranteedPhiOpt.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
//===--- OwnedToGuaranteedPhiOpt.cpp --------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// Late optimization that eliminates webs of owned phi nodes.
///
//===----------------------------------------------------------------------===//
#include "Context.h"
#include "OwnershipPhiOperand.h"
#include "Transforms.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/Defer.h"
#include "swift/Basic/STLExtras.h"
#include "swift/SILOptimizer/Utils/OwnershipOptUtils.h"
using namespace swift;
using namespace swift::semanticarc;
namespace {
using ConsumingOperandState = Context::ConsumingOperandState;
} // anonymous namespace
template <typename OperandRangeTy>
static bool canEliminatePhi(
OperandRangeTy optimizableIntroducerRange,
ArrayRef<OwnershipPhiOperand> incomingValueOperandList,
SmallVectorImpl<OwnedValueIntroducer> &ownedValueIntroducerAccumulator) {
for (auto incomingValueOperand : incomingValueOperandList) {
SILValue incomingValue = incomingValueOperand.getValue();
// Before we do anything, see if we have an incoming value with trivial
// ownership. This can occur in the case where we are working with enums due
// to trivial non-payloaded cases. Skip that.
if (incomingValue->getOwnershipKind() == OwnershipKind::None) {
continue;
}
// Then see if this is an introducer that we actually saw as able to be
// optimized if we could flip this joined live range.
//
// NOTE: If this linear search is too slow, we can change the multimap to
// sort the mapped to list by pointer instead of insertion order. In such a
// case, we could then bisect.
if (llvm::find(optimizableIntroducerRange,
incomingValueOperand.getOperand()) ==
optimizableIntroducerRange.end()) {
return false;
}
// Now that we know it is an owned value that we saw before, check for
// introducers of the owned value which are the copies that we may be able
// to eliminate. Since we do not look through joined live ranges, we must
// only have a single introducer. So look for that one and if not, bail.
auto singleIntroducer = getSingleOwnedValueIntroducer(incomingValue);
if (!singleIntroducer) {
return false;
}
// Then make sure that our owned value introducer is able to be converted to
// guaranteed and that we found it to have a LiveRange that we could have
// eliminated /if/ we were to get rid of this phi.
if (!singleIntroducer.isConvertableToGuaranteed()) {
return false;
}
// Otherwise, add the introducer to our result array.
ownedValueIntroducerAccumulator.push_back(singleIntroducer);
}
#ifndef NDEBUG
// Other parts of the pass ensure that we only add values to the list if their
// owned value introducer is not used by multiple live ranges. That being
// said, lets assert that.
{
SmallVector<OwnedValueIntroducer, 32> uniqueCheck;
llvm::copy(ownedValueIntroducerAccumulator,
std::back_inserter(uniqueCheck));
sortUnique(uniqueCheck);
assert(
uniqueCheck.size() == ownedValueIntroducerAccumulator.size() &&
"multiple joined live range operands are from the same live range?!");
}
#endif
return true;
}
static bool getIncomingJoinedLiveRangeOperands(
SILValue joinedLiveRange,
SmallVectorImpl<OwnershipPhiOperand> &resultingOperands) {
if (auto *phi = dyn_cast<SILPhiArgument>(joinedLiveRange)) {
return phi->visitIncomingPhiOperands([&](Operand *op) {
if (auto phiOp = OwnershipPhiOperand::get(op)) {
resultingOperands.push_back(*phiOp);
return true;
}
return false;
});
}
if (auto *svi = dyn_cast<SingleValueInstruction>(joinedLiveRange)) {
return llvm::all_of(svi->getAllOperands(), [&](const Operand &op) {
// skip type dependent operands.
if (op.isTypeDependent())
return true;
auto phiOp = OwnershipPhiOperand::get(&op);
if (!phiOp)
return false;
resultingOperands.push_back(*phiOp);
return true;
});
}
llvm_unreachable("Unhandled joined live range?!");
}
//===----------------------------------------------------------------------===//
// Top Level Entrypoint
//===----------------------------------------------------------------------===//
// This needs `SemanticARCOptVisitor::performGuaranteedCopyValueOptimization` to
// run before so that joinedOwnedIntroducerToConsumedOperands is populated.
bool swift::semanticarc::tryConvertOwnedPhisToGuaranteedPhis(Context &ctx) {
bool madeChange = false;
// First freeze our multi-map so we can use it for map queries. Also, setup a
// defer of the reset so we do not forget to reset the map when we are done.
ctx.joinedOwnedIntroducerToConsumedOperands.setFrozen();
SWIFT_DEFER { ctx.joinedOwnedIntroducerToConsumedOperands.reset(); };
// Now for each phi argument that we have in our multi-map...
SmallVector<OwnershipPhiOperand, 4> incomingValueOperandList;
SmallVector<OwnedValueIntroducer, 4> ownedValueIntroducers;
for (auto pair : ctx.joinedOwnedIntroducerToConsumedOperands.getRange()) {
SWIFT_DEFER {
incomingValueOperandList.clear();
ownedValueIntroducers.clear();
};
// First compute the LiveRange for ownershipPhi value. For simplicity, we
// only handle cases now where the result does not have any additional
// ownershipPhi uses.
SILValue joinedIntroducer = pair.first;
OwnershipLiveRange joinedLiveRange(joinedIntroducer);
if (bool(joinedLiveRange.hasUnknownConsumingUse())) {
continue;
}
// Ok, we know that our phi argument /could/ be converted to guaranteed if
// our incoming values are able to be converted to guaranteed. Now for each
// incoming value, compute the incoming values ownership roots and see if
// all of the ownership roots are in our owned incoming value array.
if (!getIncomingJoinedLiveRangeOperands(joinedIntroducer,
incomingValueOperandList)) {
continue;
}
// Grab our list of introducer values paired with this SILArgument. See if
// all of these introducer values were ones that /could/ have been
// eliminated if it was not for the given phi. If all of them are, we can
// optimize!
{
std::function<Operand *(const Context::ConsumingOperandState)> lambda =
[&](const Context::ConsumingOperandState &state) -> Operand * {
unsigned opNum = state.operandNumber;
if (state.parent.is<SILBasicBlock *>()) {
SILBasicBlock *block = state.parent.get<SILBasicBlock *>();
return &block->getTerminator()->getAllOperands()[opNum];
}
SILInstruction *inst = state.parent.get<SILInstruction *>();
return &inst->getAllOperands()[opNum];
};
auto operandsTransformed = makeTransformRange(pair.second, lambda);
if (!canEliminatePhi(operandsTransformed, incomingValueOperandList,
ownedValueIntroducers)) {
continue;
}
}
// Ok, at this point we know that we can eliminate this phi. First go
// through the list of incomingValueOperandList and stash the value/set the
// operand's stored value to undef. We will hook them back up later.
SmallVector<SILValue, 8> originalIncomingValues;
for (auto &incomingValueOperand : incomingValueOperandList) {
originalIncomingValues.push_back(incomingValueOperand.getValue());
incomingValueOperand.markUndef();
}
// Then go through all of our owned value introducers, compute their live
// ranges, and eliminate them. We know it is safe to remove them from our
// previous proofs.
//
// NOTE: If our introducer is a copy_value that is one of our
// originalIncomingValues, we need to update the originalIncomingValue array
// with that value since we are going to delete the copy_value here. This
// creates a complication since we want to binary_search on
// originalIncomingValues to detect this same condition! So, we create a
// list of updates that we apply after we no longer need to perform
// binary_search, but before we start RAUWing things.
SmallVector<std::pair<SILValue, unsigned>, 8> incomingValueUpdates;
for (auto introducer : ownedValueIntroducers) {
SILValue v = introducer.value;
OwnershipLiveRange lr(v);
// For now, we only handle copy_value for simplicity.
//
// TODO: Add support for load [copy].
if (introducer.kind == OwnedValueIntroducerKind::Copy) {
auto *cvi = cast<CopyValueInst>(v);
// Before we convert from owned to guaranteed, we need to first see if
// cvi is one of our originalIncomingValues. If so, we need to set
// originalIncomingValues to be cvi->getOperand(). Otherwise, weirdness
// results since we are deleting one of our stashed values.
auto iter = find(originalIncomingValues, cvi);
if (iter != originalIncomingValues.end()) {
// We use an auxillary array here so we can continue to bisect on
// original incoming values. Once we are done processing here, we will
// not need that property anymore.
unsigned updateOffset =
std::distance(originalIncomingValues.begin(), iter);
incomingValueUpdates.emplace_back(cvi->getOperand(), updateOffset);
}
std::move(lr).convertToGuaranteedAndRAUW(cvi->getOperand(),
ctx.instModCallbacks);
continue;
}
llvm_unreachable("Unhandled optimizable introducer!");
}
// Now go through and update our original incoming value array now that we
// do not need it to be sorted for bisection purposes.
while (!incomingValueUpdates.empty()) {
auto pair = incomingValueUpdates.pop_back_val();
originalIncomingValues[pair.second] = pair.first;
}
// Now if our phi operand consumes/forwards its guaranteed input, insert a
// begin_borrow along the incoming value edges. We have to do this after
// converting the incoming values to be guaranteed to avoid tripping
// SILBuilder checks around simple ownership invariants (namely that def/use
// line up) when creating instructions.
assert(incomingValueOperandList.size() == originalIncomingValues.size());
while (!incomingValueOperandList.empty()) {
auto incomingValueOperand = incomingValueOperandList.pop_back_val();
SILValue originalValue = originalIncomingValues.pop_back_val();
incomingValueOperand.getOperand()->set(originalValue);
}
// Then convert the phi's live range to be guaranteed.
std::move(joinedLiveRange)
.convertJoinedLiveRangePhiToGuaranteed(
ctx.getDeadEndBlocks(), ctx.lifetimeFrontier, ctx.instModCallbacks);
madeChange = true;
ctx.verify();
}
if (madeChange)
updateAllGuaranteedPhis(ctx.pm, &ctx.fn);
return madeChange;
}