-
Notifications
You must be signed in to change notification settings - Fork 10.4k
/
Copy pathSILLowerAggregateInstrs.cpp
338 lines (289 loc) · 11.5 KB
/
SILLowerAggregateInstrs.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
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
//===--- SILLowerAggregateInstrs.cpp - Aggregate insts to Scalar insts ---===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
///
/// Simplify aggregate instructions into scalar instructions using simple
/// peephole transformations.
///
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "sil-lower-aggregate-instrs"
#include "swift/Basic/Assertions.h"
#include "swift/SIL/Projection.h"
#include "swift/SIL/SILBuilder.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILModule.h"
#include "swift/SIL/SILVisitor.h"
#include "swift/SIL/TypeLowering.h"
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
using namespace swift;
using namespace swift::Lowering;
STATISTIC(NumExpand, "Number of instructions expanded");
static llvm::cl::opt<bool> EnableExpandAll("sil-lower-agg-instrs-expand-all",
llvm::cl::init(false));
//===----------------------------------------------------------------------===//
// Utility
//===----------------------------------------------------------------------===//
/// We only expand if we are not in ownership and shouldExpand is true. The
/// reason why is that this was originally done to help the low level ARC
/// optimizer. To the high level ARC optimizer, this is just noise and
/// unnecessary IR. At the same time for testing purposes, we want to provide a
/// way even with ownership enabled to expand so we can check correctness.
static bool shouldExpandShim(SILFunction *fn, SILType type) {
// shouldExpand returns false for struct-with-deinit types, so bypassing it is
// incorrect for move-only types
if (EnableExpandAll) {
assert(!type.isMoveOnly(/*orWrapped=*/false)
&& "sil-lower-agg-instrs-expand-all is incompatible with move-only "
"types");
return true;
}
return !fn->hasOwnership() && shouldExpand(fn->getModule(), type);
}
//===----------------------------------------------------------------------===//
// Higher Level Operation Expansion
//===----------------------------------------------------------------------===//
/// Lower copy_addr into loads/stores/retain/release if we have a
/// non-address only type. We do this here so we can process the resulting
/// loads/stores.
///
/// This peephole implements the following optimizations with the ossa version
/// of the optimization first.
///
/// copy_addr %0 to %1 : $*T
/// ->
/// %new = load [copy] %0 : $*T
/// store %new to [assign] %1 : $*T
/// ->
/// %new = load %0 : $*T // Load the new value from the source
/// strong_retain %new : $T // Retain the new value
/// %old = load %1 : $*T // Load the old value from the destination
/// strong_release %old : $T // Release the old
/// store %new to %1 : $*T // Store the new value to the destination
///
/// copy_addr [take] %0 to %1 : $*T
/// ->
/// // load [take], not load [copy]!
/// %new = load [take] %0 : $*T
/// store %new to [assign] %1 : $*T
/// ->
/// %new = load %0 : $*T
/// // no retain of %new!
/// %old = load %1 : $*T
/// strong_release %old : $T
/// store %new to %1 : $*T
///
/// copy_addr %0 to [init] %1 : $*T
/// ->
/// %new = load [copy] %0 : $*T
/// store %new to [init] %1 : $*T
/// ->
/// %new = load %0 : $*T
/// strong_retain %new : $T
/// // no load/release of %old!
/// store %new to %1 : $*T
///
/// copy_addr [take] %0 to [init] %1 : $*T
/// ->
/// %new = load [take] %0 : $*T
/// store %new to [init] %1 : $*T
/// ->
/// %new = load %0 : $*T
/// // no retain of %new!
/// // no load/release of %old!
/// store %new to %1 : $*T
static bool expandCopyAddr(CopyAddrInst *cai) {
SILFunction *fn = cai->getFunction();
SILValue source = cai->getSrc();
// If we have an address only type don't do anything.
SILType srcType = source->getType();
if (srcType.isAddressOnly(*fn))
return false;
bool expand = shouldExpandShim(fn, srcType.getObjectType());
using TypeExpansionKind = Lowering::TypeLowering::TypeExpansionKind;
auto expansionKind = expand ? TypeExpansionKind::MostDerivedDescendents
: TypeExpansionKind::None;
SILBuilderWithScope builder(cai);
// If our object type is not trivial, we may need to destroy the old value and
// copy the new one. Handle the trivial case quickly and return.
if (srcType.isTrivial(*fn)) {
SILValue newValue = builder.emitLoadValueOperation(
cai->getLoc(), source, LoadOwnershipQualifier::Trivial);
SILValue destAddr = cai->getDest();
// Create the store.
builder.emitStoreValueOperation(cai->getLoc(), newValue, destAddr,
StoreOwnershipQualifier::Trivial);
++NumExpand;
return true;
}
// %new = load [copy|take] %0 : $*T
auto loadQual = [&]() -> LoadOwnershipQualifier {
if (IsTake_t::IsTake == cai->isTakeOfSrc())
return LoadOwnershipQualifier::Take;
return LoadOwnershipQualifier::Copy;
}();
SILValue newValue = builder.emitLoweredLoadValueOperation(
cai->getLoc(), source, loadQual, expansionKind);
SILValue destAddr = cai->getDest();
// Create the store in the guaranteed uninitialized memory.
//
// store %new to [init|assign] %1
//
// If we are not initializing the destination, we need to destroy what is
// currently there before we re-initialize the memory.
auto storeQualifier = [&]() -> StoreOwnershipQualifier {
if (IsInitialization_t::IsInitialization != cai->isInitializationOfDest())
return StoreOwnershipQualifier::Assign;
return StoreOwnershipQualifier::Init;
}();
builder.emitLoweredStoreValueOperation(cai->getLoc(), newValue, destAddr,
storeQualifier, expansionKind);
++NumExpand;
return true;
}
static bool expandDestroyAddr(DestroyAddrInst *dai) {
SILFunction *fn = dai->getFunction();
SILBuilderWithScope builder(dai);
// Strength reduce destroy_addr inst into release/store if
// we have a non-address only type.
SILValue addr = dai->getOperand();
// If we have an address only type, do nothing.
SILType type = addr->getType();
if (type.isAddressOnly(*fn))
return false;
// We only expand if ownership is not enabled and we do not have a large
// type. This was something that was only really beneficial for the low level
// ARC optimizer which runs without ownership enabled.
bool expand = shouldExpandShim(fn, type.getObjectType());
// If we have a non-trivial type...
if (!type.isTrivial(*fn)) {
// If we have a type with reference semantics, emit a load/destroy.
SILValue li = builder.emitLoadValueOperation(dai->getLoc(), addr,
LoadOwnershipQualifier::Take);
auto &typeLowering = fn->getTypeLowering(type);
using TypeExpansionKind = Lowering::TypeLowering::TypeExpansionKind;
auto expansionKind = expand ? TypeExpansionKind::MostDerivedDescendents
: TypeExpansionKind::None;
typeLowering.emitLoweredDestroyValue(builder, dai->getLoc(), li,
expansionKind);
}
++NumExpand;
return true;
}
static bool expandReleaseValue(ReleaseValueInst *rvi) {
SILFunction *fn = rvi->getFunction();
SILBuilderWithScope builder(rvi);
// Strength reduce destroy_addr inst into release/store if
// we have a non-address only type.
SILValue value = rvi->getOperand();
// If we have an address only type, do nothing.
SILType type = value->getType();
assert(!SILModuleConventions(fn->getModule()).useLoweredAddresses() ||
type.isLoadable(*fn) &&
"release_value should never be called on a non-loadable type.");
if (!shouldExpandShim(fn, type.getObjectType()))
return false;
auto &TL = fn->getTypeLowering(type);
TL.emitLoweredDestroyValueMostDerivedDescendents(builder, rvi->getLoc(),
value);
LLVM_DEBUG(llvm::dbgs() << " Expanding: " << *rvi);
++NumExpand;
return true;
}
static bool expandRetainValue(RetainValueInst *rvi) {
SILFunction *fn = rvi->getFunction();
SILBuilderWithScope builder(rvi);
// Strength reduce destroy_addr inst into release/store if
// we have a non-address only type.
SILValue value = rvi->getOperand();
// If we have an address only type, do nothing.
SILType type = value->getType();
assert(!SILModuleConventions(fn->getModule()).useLoweredAddresses() ||
type.isLoadable(*fn) &&
"Copy Value can only be called on loadable types.");
if (!shouldExpandShim(fn, type.getObjectType()))
return false;
auto &typeLowering = fn->getTypeLowering(type);
typeLowering.emitLoweredCopyValueMostDerivedDescendents(builder,
rvi->getLoc(), value);
LLVM_DEBUG(llvm::dbgs() << " Expanding: " << *rvi);
++NumExpand;
return true;
}
//===----------------------------------------------------------------------===//
// Top Level Driver
//===----------------------------------------------------------------------===//
static bool processFunction(SILFunction &fn) {
bool changed = false;
for (auto &block : fn) {
auto ii = block.begin(), ie = block.end();
while (ii != ie) {
SILInstruction *inst = &*ii;
LLVM_DEBUG(llvm::dbgs() << "Visiting: " << *inst);
if (auto *cai = dyn_cast<CopyAddrInst>(inst))
if (expandCopyAddr(cai)) {
++ii;
cai->eraseFromParent();
changed = true;
continue;
}
if (auto *dai = dyn_cast<DestroyAddrInst>(inst))
if (expandDestroyAddr(dai)) {
++ii;
dai->eraseFromParent();
changed = true;
continue;
}
if (auto *rvi = dyn_cast<RetainValueInst>(inst))
if (expandRetainValue(rvi)) {
++ii;
rvi->eraseFromParent();
changed = true;
continue;
}
if (auto *rvi = dyn_cast<ReleaseValueInst>(inst))
if (expandReleaseValue(rvi)) {
++ii;
rvi->eraseFromParent();
changed = true;
continue;
}
++ii;
}
}
return changed;
}
//===----------------------------------------------------------------------===//
// Top Level Entrypoint
//===----------------------------------------------------------------------===//
namespace {
class SILLowerAggregate : public SILFunctionTransform {
/// The entry point to the transformation.
void run() override {
SILFunction *f = getFunction();
LLVM_DEBUG(llvm::dbgs() << "***** LowerAggregate on function: "
<< f->getName() << " *****\n");
bool changed = processFunction(*f);
if (changed) {
invalidateAnalysis(SILAnalysis::InvalidationKind::CallsAndInstructions);
}
}
};
} // end anonymous namespace
SILTransform *swift::createLowerAggregateInstrs() {
return new SILLowerAggregate();
}