@@ -27,23 +27,26 @@ class ObjectOutliner {
27
27
NominalTypeDecl *ArrayDecl = nullptr ;
28
28
int GlobIdx = 0 ;
29
29
30
+ // Instructions to be deleted.
31
+ llvm::SmallVector<SILInstruction *, 4 > ToRemove;
32
+
30
33
bool isCOWType (SILType type) {
31
34
return type.getNominalOrBoundGenericNominal () == ArrayDecl;
32
35
}
33
36
34
- bool isValidUseOfObject (SILInstruction *Val, bool isCOWObject,
37
+ bool isValidUseOfObject (SILInstruction *Val,
38
+ bool isCOWObject,
35
39
ApplyInst **FindStringCall = nullptr );
36
40
37
41
bool getObjectInitVals (SILValue Val,
38
42
llvm::DenseMap<VarDecl *, StoreInst *> &MemberStores,
39
43
llvm::SmallVectorImpl<StoreInst *> &TailStores,
44
+ unsigned NumTailTupleElements,
40
45
ApplyInst **FindStringCall);
41
- bool handleTailAddr (int TailIdx, SILInstruction *I,
46
+ bool handleTailAddr (int TailIdx, SILInstruction *I, unsigned NumTailTupleElements,
42
47
llvm::SmallVectorImpl<StoreInst *> &TailStores);
43
48
44
- bool
45
- optimizeObjectAllocation (AllocRefInst *ARI,
46
- llvm::SmallVector<SILInstruction *, 4 > &ToRemove);
49
+ bool optimizeObjectAllocation (AllocRefInst *ARI);
47
50
void replaceFindStringCall (ApplyInst *FindStringCall);
48
51
49
52
public:
@@ -60,23 +63,27 @@ bool ObjectOutliner::run(SILFunction *F) {
60
63
for (auto &BB : *F) {
61
64
auto Iter = BB.begin ();
62
65
63
- // We can't remove instructions willy-nilly as we iterate because
64
- // that might cause a pointer to the next instruction to become
65
- // garbage, causing iterator invalidations (and crashes).
66
- // Instead, we collect in a list the instructions we want to remove
67
- // and erase the BB they belong to at the end of the loop, once we're
68
- // sure it's safe to do so.
69
- llvm::SmallVector<SILInstruction *, 4 > ToRemove;
70
-
71
66
while (Iter != BB.end ()) {
72
67
SILInstruction *I = &*Iter;
73
68
Iter++;
74
69
if (auto *ARI = dyn_cast<AllocRefInst>(I)) {
75
- hasChanged |= optimizeObjectAllocation (ARI, ToRemove);
70
+ unsigned GarbageSize = ToRemove.size ();
71
+
72
+ // Try to replace the alloc_ref with a static object.
73
+ if (optimizeObjectAllocation (ARI)) {
74
+ hasChanged = true ;
75
+ } else {
76
+ // No transformation was made. Restore the original state of the garbage list.
77
+ assert (GarbageSize <= ToRemove.size ());
78
+ ToRemove.resize (GarbageSize);
79
+ }
76
80
}
77
81
}
82
+ // Delaying the deallocation of instructions avoids problems with iterator invalidation in the
83
+ // instruction loop above.
78
84
for (auto *I : ToRemove)
79
85
I->eraseFromParent ();
86
+ ToRemove.clear ();
80
87
}
81
88
return hasChanged;
82
89
}
@@ -177,6 +184,13 @@ bool ObjectOutliner::isValidUseOfObject(SILInstruction *I, bool isCOWObject,
177
184
BuiltinValueKind K = BI->getBuiltinInfo ().ID ;
178
185
if (K == BuiltinValueKind::ICMP_EQ || K == BuiltinValueKind::ICMP_NE)
179
186
return true ;
187
+ if (K == BuiltinValueKind::DestroyArray) {
188
+ // We must not try to delete the tail allocated values. Although this would be a no-op
189
+ // (because we only handle trivial types), it would be semantically wrong to apply this
190
+ // builtin on the outlined object.
191
+ ToRemove.push_back (BI);
192
+ return true ;
193
+ }
180
194
return false ;
181
195
}
182
196
@@ -194,14 +208,28 @@ bool ObjectOutliner::isValidUseOfObject(SILInstruction *I, bool isCOWObject,
194
208
195
209
// / Handle the address of a tail element.
196
210
bool ObjectOutliner::handleTailAddr (int TailIdx, SILInstruction *TailAddr,
197
- llvm::SmallVectorImpl<StoreInst *> &TailStores) {
198
- if (TailIdx >= 0 && TailIdx < (int )TailStores.size ()) {
199
- if (auto *SI = dyn_cast<StoreInst>(TailAddr)) {
200
- if (!isValidInitVal (SI->getSrc ()) || TailStores[TailIdx])
201
- return false ;
202
- TailStores[TailIdx] = SI;
211
+ unsigned NumTailTupleElements,
212
+ llvm::SmallVectorImpl<StoreInst *> &TailStores) {
213
+ if (NumTailTupleElements > 0 ) {
214
+ if (auto *TEA = dyn_cast<TupleElementAddrInst>(TailAddr)) {
215
+ unsigned TupleIdx = TEA->getFieldNo ();
216
+ assert (TupleIdx < NumTailTupleElements);
217
+ for (Operand *Use : TEA->getUses ()) {
218
+ if (!handleTailAddr (TailIdx * NumTailTupleElements + TupleIdx, Use->getUser (), 0 ,
219
+ TailStores))
220
+ return false ;
221
+ }
203
222
return true ;
204
223
}
224
+ } else {
225
+ if (TailIdx >= 0 && TailIdx < (int )TailStores.size ()) {
226
+ if (auto *SI = dyn_cast<StoreInst>(TailAddr)) {
227
+ if (!isValidInitVal (SI->getSrc ()) || TailStores[TailIdx])
228
+ return false ;
229
+ TailStores[TailIdx] = SI;
230
+ return true ;
231
+ }
232
+ }
205
233
}
206
234
return isValidUseOfObject (TailAddr, /* isCOWObject*/ false );
207
235
}
@@ -210,12 +238,13 @@ bool ObjectOutliner::handleTailAddr(int TailIdx, SILInstruction *TailAddr,
210
238
bool ObjectOutliner::getObjectInitVals (SILValue Val,
211
239
llvm::DenseMap<VarDecl *, StoreInst *> &MemberStores,
212
240
llvm::SmallVectorImpl<StoreInst *> &TailStores,
241
+ unsigned NumTailTupleElements,
213
242
ApplyInst **FindStringCall) {
214
243
for (Operand *Use : Val->getUses ()) {
215
244
SILInstruction *User = Use->getUser ();
216
245
if (auto *UC = dyn_cast<UpcastInst>(User)) {
217
246
// Upcast is transparent.
218
- if (!getObjectInitVals (UC, MemberStores, TailStores, FindStringCall))
247
+ if (!getObjectInitVals (UC, MemberStores, TailStores, NumTailTupleElements, FindStringCall))
219
248
return false ;
220
249
} else if (auto *REA = dyn_cast<RefElementAddrInst>(User)) {
221
250
// The address of a stored property.
@@ -242,11 +271,11 @@ bool ObjectOutliner::getObjectInitVals(SILValue Val,
242
271
TailIdx = Index->getValue ().getZExtValue ();
243
272
244
273
for (Operand *IAUse : IA->getUses ()) {
245
- if (!handleTailAddr (TailIdx, IAUse->getUser (), TailStores))
274
+ if (!handleTailAddr (TailIdx, IAUse->getUser (), NumTailTupleElements, TailStores))
246
275
return false ;
247
276
}
248
277
// Without an index_addr it's the first tail element.
249
- } else if (!handleTailAddr (/* TailIdx*/ 0 , TailUser, TailStores)) {
278
+ } else if (!handleTailAddr (/* TailIdx*/ 0 , TailUser, NumTailTupleElements, TailStores)) {
250
279
return false ;
251
280
}
252
281
}
@@ -280,9 +309,7 @@ class GlobalVariableMangler : public Mangle::ASTMangler {
280
309
// / func getarray() -> [Int] {
281
310
// / return [1, 2, 3]
282
311
// / }
283
- bool ObjectOutliner::optimizeObjectAllocation (
284
- AllocRefInst *ARI, llvm::SmallVector<SILInstruction *, 4 > &ToRemove) {
285
-
312
+ bool ObjectOutliner::optimizeObjectAllocation (AllocRefInst *ARI) {
286
313
if (ARI->isObjC ())
287
314
return false ;
288
315
@@ -316,13 +343,30 @@ bool ObjectOutliner::optimizeObjectAllocation(
316
343
llvm::SmallVector<VarDecl *, 16 > Fields;
317
344
getFields (Cl, Fields);
318
345
319
- // Get the initialization stores of the object's properties and tail
320
- // allocated elements. Also check if there are any "bad" uses of the object.
321
346
llvm::DenseMap<VarDecl *, StoreInst *> MemberStores;
347
+
348
+ // A store for each element of the tail allocated array. In case of a tuple, there is a store
349
+ // for each tuple element. For example, a 3 element array of 2-element tuples
350
+ // [ (i0, i1), (i2, i3), (i4, i5) ]
351
+ // results in following store instructions, collected in TailStores:
352
+ // [ store i0, store i1, store i2, store i3, store i4, store i5 ]
322
353
llvm::SmallVector<StoreInst *, 16 > TailStores;
323
- TailStores.resize (NumTailElems);
354
+
355
+ unsigned NumStores = NumTailElems;
356
+ unsigned NumTailTupleElems = 0 ;
357
+ if (auto Tuple = TailType.getAs <TupleType>()) {
358
+ NumTailTupleElems = Tuple->getNumElements ();
359
+ if (NumTailTupleElems == 0 )
360
+ return false ;
361
+ NumStores *= NumTailTupleElems;
362
+ }
363
+
364
+ TailStores.resize (NumStores);
324
365
ApplyInst *FindStringCall = nullptr ;
325
- if (!getObjectInitVals (ARI, MemberStores, TailStores, &FindStringCall))
366
+
367
+ // Get the initialization stores of the object's properties and tail
368
+ // allocated elements. Also check if there are any "bad" uses of the object.
369
+ if (!getObjectInitVals (ARI, MemberStores, TailStores, NumTailTupleElems, &FindStringCall))
326
370
return false ;
327
371
328
372
// Is there a store for all the class properties?
@@ -372,13 +416,32 @@ bool ObjectOutliner::optimizeObjectAllocation(
372
416
cast<SingleValueInstruction>(MemberStore->getSrc ())));
373
417
ToRemove.push_back (MemberStore);
374
418
}
375
- // Create the initializers for the tail elements.
376
419
unsigned NumBaseElements = ObjectArgs.size ();
377
- for (StoreInst *TailStore : TailStores) {
378
- ObjectArgs.push_back (Cloner.clone (
379
- cast<SingleValueInstruction>(TailStore->getSrc ())));
380
- ToRemove.push_back (TailStore);
420
+
421
+ // Create the initializers for the tail elements.
422
+ if (NumTailTupleElems == 0 ) {
423
+ // The non-tuple element case.
424
+ for (StoreInst *TailStore : TailStores) {
425
+ ObjectArgs.push_back (Cloner.clone (
426
+ cast<SingleValueInstruction>(TailStore->getSrc ())));
427
+ ToRemove.push_back (TailStore);
428
+ }
429
+ } else {
430
+ // The elements are tuples: combine NumTailTupleElems elements from TailStores to a single tuple
431
+ // instruction.
432
+ for (unsigned EIdx = 0 ; EIdx < NumTailElems; EIdx++) {
433
+ SmallVector<SILValue, 8 > TupleElems;
434
+ for (unsigned TIdx = 0 ; TIdx < NumTailTupleElems; TIdx++) {
435
+ StoreInst *TailStore = TailStores[EIdx * NumTailTupleElems + TIdx];
436
+ SILValue V = Cloner.clone (cast<SingleValueInstruction>(TailStore->getSrc ()));
437
+ TupleElems.push_back (V);
438
+ ToRemove.push_back (TailStore);
439
+ }
440
+ auto *TI = Cloner.getBuilder ().createTuple (ARI->getLoc (), TailType, TupleElems);
441
+ ObjectArgs.push_back (TI);
442
+ }
381
443
}
444
+
382
445
// Create the initializer for the object itself.
383
446
SILBuilder StaticInitBuilder (Glob);
384
447
StaticInitBuilder.createObject (ArtificialUnreachableLocation (),
0 commit comments